From bff54b27073bb37952136f667139d2e03475a63a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 18 Nov 2024 12:01:05 +0800 Subject: [PATCH 001/941] Bump version to 9.0.0-SNAPSHOT --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e8d2c2537..c64924434 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.configuration-cache.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=8.3.6-SNAPSHOT +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 02f40fdd6bcefb9cf34abdcbd1648d5051e15e13 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 18 Nov 2024 03:00:01 -0500 Subject: [PATCH 002/941] Prepare Kotlin migration (#1017) --- api/build.gradle.kts | 47 ++++++++++++++++++++++++++++++++++++++++++++ build.gradle.kts | 16 +++++++++++++++ settings.gradle.kts | 3 +++ 3 files changed, 66 insertions(+) create mode 100644 api/build.gradle.kts diff --git a/api/build.gradle.kts b/api/build.gradle.kts new file mode 100644 index 000000000..8594b8a60 --- /dev/null +++ b/api/build.gradle.kts @@ -0,0 +1,47 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion + +plugins { + kotlin("jvm") + `java-gradle-plugin` + id("com.diffplug.spotless") +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +kotlin { + compilerOptions { + // https://docs.gradle.org/current/userguide/compatibility.html#kotlin + apiVersion = KotlinVersion.KOTLIN_1_8 + jvmTarget = JvmTarget.JVM_1_8 + freeCompilerArgs.addAll( + "-Xjvm-default=all", + ) + } +} + +spotless { + kotlin { + ktlint().editorConfigOverride( + mapOf( + "indent_size" to "4", + ), + ) + } +} + +dependencies { + compileOnly("com.gradleup.shadow:shadow-gradle-plugin:8.3.5") + + implementation("org.jdom:jdom2:2.0.6.1") + implementation("org.ow2.asm:asm-commons:9.7.1") + implementation("commons-io:commons-io:2.17.0") + implementation("org.apache.ant:ant:1.10.15") + implementation("org.codehaus.plexus:plexus-utils:4.0.2") + implementation("org.codehaus.plexus:plexus-xml:4.0.4") + implementation("org.apache.logging.log4j:log4j-core:2.24.1") + implementation("org.vafer:jdependency:2.11") +} diff --git a/build.gradle.kts b/build.gradle.kts index 1055e210d..30d657f77 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,8 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion + plugins { + kotlin("jvm") version "2.0.21" groovy `java-gradle-plugin` id("shadow.convention.publish") @@ -11,6 +15,17 @@ java { targetCompatibility = JavaVersion.VERSION_1_8 } +kotlin { + compilerOptions { + // https://docs.gradle.org/current/userguide/compatibility.html#kotlin + apiVersion = KotlinVersion.KOTLIN_1_8 + jvmTarget = JvmTarget.JVM_1_8 + freeCompilerArgs.addAll( + "-Xjvm-default=all", + ) + } +} + spotless { kotlinGradle { ktlint() @@ -21,6 +36,7 @@ spotless { dependencies { compileOnly(localGroovy()) + implementation(projects.api) implementation("org.jdom:jdom2:2.0.6.1") implementation("org.ow2.asm:asm-commons:9.7.1") diff --git a/settings.gradle.kts b/settings.gradle.kts index 87569ed1f..a76e1195b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,3 +30,6 @@ dependencyResolutionManagement { rootProject.name = "shadow" enableFeaturePreview("STABLE_CONFIGURATION_CACHE") +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") + +include(":api") From 45218879e4e43e4490e6a249e6aa4899143f0b49 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 18 Nov 2024 04:09:53 -0500 Subject: [PATCH 003/941] Kotlin migration part 1 (#1018) * Convert LegacyShadowPlugin * Convert ShadowExtension * Convert ShadowPlugin * Convert ShadowBasePlugin * Convert CacheableRelocator * Convert Relocator * Convert RelocateClassContext and RelocatePathContext * Convert DependencyFilter * Convert InheritManifest * Convert ShadowSpec * Convert Transformer and add NoOpTransformer * Convert CacheableTransformer --- .../gradle/plugins/shadow/ShadowBasePlugin.kt | 31 ++++++++ .../gradle/plugins/shadow/ShadowExtension.kt | 14 ++++ .../gradle/plugins/shadow/ShadowPlugin.kt | 28 +++++++ .../shadow/internal/DependencyFilter.kt | 54 +++++++++++++ .../shadow/legacy/LegacyShadowPlugin.kt | 9 +-- .../shadow/relocation/CacheableRelocator.kt | 10 +++ .../shadow/relocation/RelocateClassContext.kt | 22 ++++++ .../shadow/relocation/RelocatePathContext.kt | 22 ++++++ .../plugins/shadow/relocation/Relocator.kt | 23 ++++++ .../plugins/shadow/tasks/InheritManifest.kt | 10 +++ .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 71 ++++++++++++++++++ .../transformers/CacheableTransformer.kt | 10 +++ .../shadow/transformers/Transformer.kt | 33 ++++++++ .../plugins/shadow/ShadowBasePlugin.groovy | 32 -------- .../plugins/shadow/ShadowExtension.groovy | 22 ------ .../gradle/plugins/shadow/ShadowPlugin.groovy | 30 -------- .../internal/AbstractDependencyFilter.groovy | 20 ++--- .../shadow/internal/DependencyFilter.groovy | 75 ------------------- .../relocation/CacheableRelocator.groovy | 17 ----- .../relocation/RelocateClassContext.groovy | 14 ---- .../relocation/RelocatePathContext.groovy | 13 ---- .../shadow/relocation/Relocator.groovy | 40 ---------- .../tasks/DefaultInheritManifest.groovy | 6 +- .../shadow/tasks/InheritManifest.groovy | 10 --- .../plugins/shadow/tasks/ShadowJar.java | 10 +-- .../plugins/shadow/tasks/ShadowSpec.java | 48 ------------ .../transformers/CacheableTransformer.groovy | 17 ----- .../shadow/transformers/Transformer.groovy | 48 ------------ 28 files changed, 350 insertions(+), 389 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt rename src/main/groovy/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.groovy => api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt (51%) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.java delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.groovy diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt new file mode 100644 index 000000000..94c8a051e --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -0,0 +1,31 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.tasks.KnowsTask +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.util.GradleVersion + +abstract class ShadowBasePlugin : Plugin { + + override fun apply(project: Project) { + if (GradleVersion.current() < GradleVersion.version("8.3")) { + throw GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.") + } + project.extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) + project.configurations.create(CONFIGURATION_NAME) + project.tasks.register(KnowsTask.NAME, KnowsTask::class.java) { knows -> + knows.group = GROUP_NAME + knows.description = KnowsTask.DESC + } + } + + companion object { + const val SHADOW: String = "shadow" + const val GROUP_NAME: String = SHADOW + const val EXTENSION_NAME: String = SHADOW + const val CONFIGURATION_NAME: String = SHADOW + const val COMPONENT_NAME: String = SHADOW + const val DISTRIBUTION_NAME: String = SHADOW + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt new file mode 100644 index 000000000..6b22883a1 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -0,0 +1,14 @@ +package com.github.jengelman.gradle.plugins.shadow + +import org.gradle.api.Project +import org.gradle.api.publish.maven.MavenPublication + +@Deprecated("This is deprecated since 8.3.2") +abstract class ShadowExtension(project: Project) { + private val components = project.components + + @Deprecated("configure publication using component.shadow directly.") + fun component(publication: MavenPublication) { + publication.from(components.findByName(ShadowBasePlugin.COMPONENT_NAME)) + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt new file mode 100644 index 000000000..0b5c58e3c --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt @@ -0,0 +1,28 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.plugins.ApplicationPlugin +import org.gradle.api.plugins.JavaPlugin + +abstract class ShadowPlugin : Plugin { + + override fun apply(project: Project) { + project.run { + plugins.apply(ShadowBasePlugin::class.java) + plugins.withType(JavaPlugin::class.java) { + plugins.apply(ShadowJavaPlugin::class.java) + } + plugins.withType(ApplicationPlugin::class.java) { + plugins.apply(ShadowApplicationPlugin::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. + // If the user applies shadow before those plugins. However, this is fine, because this was also + // the behavior with the old plugin when applying in that order. + plugins.apply(LegacyShadowPlugin::class.java) + } + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt new file mode 100644 index 000000000..993165cf0 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt @@ -0,0 +1,54 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import groovy.lang.Closure +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.file.FileCollection +import org.gradle.api.specs.Spec + +interface DependencyFilter { + /** + * Resolve a FileCollection against the include/exclude rules in the filter. + */ + fun resolve(configuration: FileCollection): FileCollection + + /** + * Resolve all FileCollections against the include/exclude rules in the filter and combine the results. + */ + fun resolve(configurations: Collection): FileCollection + + /** + * Exclude dependencies that match the provided spec. + */ + fun exclude(spec: Spec): DependencyFilter + + /** + * Include dependencies that match the provided spec. + */ + fun include(spec: Spec): DependencyFilter + + /** + * Create a spec that matches the provided project notation on group, name, and version. + */ + fun project(notation: Map): Spec + + /** + * Create a spec that matches the default configuration for the provided project path on group, name, and version. + */ + fun project(notation: String): Spec + + /** + * Create a spec that matches dependencies using the provided notation on group, name, and version. + */ + fun dependency(notation: Any): Spec + + /** + * Create a spec that matches the provided dependency on group, name, and version. + */ + fun dependency(dependency: Dependency): Spec + + /** + * Create a spec that matches the provided closure. + */ + fun dependency(closure: Closure<*>): Spec +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.groovy b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt similarity index 51% rename from src/main/groovy/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.groovy rename to api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt index e3e69a7ef..834ee364d 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.groovy +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt @@ -4,13 +4,10 @@ import org.gradle.api.Plugin import org.gradle.api.Project /** - * Empty plugin to still have the com.github.johnrengelman.shadow plugin applied. + * Empty plugin to still have the `com.github.johnrengelman.shadow` plugin applied. * * This allows older build logic to keep on working as if that old plugin ID was applied. */ -class LegacyShadowPlugin implements Plugin { - @Override - void apply(Project target) { - // Do nothing - } +abstract class LegacyShadowPlugin : Plugin { + override fun apply(project: Project): Unit = Unit } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt new file mode 100644 index 000000000..cde63e424 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt @@ -0,0 +1,10 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +/** + * Marks that a given instance of [Relocator] is compatible with the Gradle build cache. + * In other words, it has its appropriate inputs annotated so that Gradle can consider them when + * determining the cache key. + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +annotation class CacheableRelocator diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt new file mode 100644 index 000000000..8d3b321e5 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt @@ -0,0 +1,22 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats + +data class RelocateClassContext( + val className: String, + val stats: ShadowStats, +) { + class Builder { + private var className = "" + private var stats = ShadowStats() + + fun className(className: String): Builder = apply { this.className = className } + fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + fun build(): RelocateClassContext = RelocateClassContext(className, stats) + } + + companion object { + @JvmStatic + fun builder(): Builder = Builder() + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt new file mode 100644 index 000000000..87babb5d6 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt @@ -0,0 +1,22 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats + +data class RelocatePathContext( + val path: String, + val stats: ShadowStats, +) { + class Builder { + private var path = "" + private var stats = ShadowStats() + + fun path(path: String): Builder = apply { this.path = path } + fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + fun build(): RelocatePathContext = RelocatePathContext(path, stats) + } + + companion object { + @JvmStatic + fun builder(): Builder = Builder() + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt new file mode 100644 index 000000000..d754ba7a1 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -0,0 +1,23 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +/** + * Modified from `org.apache.maven.plugins.shade.relocation.Relocator.java` + * + * @author Jason van Zyl + * @author John Engelman + */ +interface Relocator { + fun canRelocatePath(path: String): Boolean + + fun relocatePath(context: RelocatePathContext): String + + fun canRelocateClass(className: String): Boolean + + fun relocateClass(context: RelocateClassContext): String + + fun applyToSourceContent(sourceContent: String): String + + companion object { + val ROLE: String = Relocator::class.java.name + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt new file mode 100644 index 000000000..d14ecb18f --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt @@ -0,0 +1,10 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import org.gradle.api.Action +import org.gradle.api.java.archives.Manifest + +interface InheritManifest : Manifest { + fun inheritFrom(vararg inheritPaths: Any): InheritManifest + + fun inheritFrom(vararg inheritPaths: Any, action: Action<*>?): InheritManifest +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt new file mode 100644 index 000000000..04caf7315 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -0,0 +1,71 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import java.lang.reflect.InvocationTargetException +import org.gradle.api.Action +import org.gradle.api.file.CopySpec + +interface ShadowSpec : CopySpec { + fun minimize(): ShadowSpec + + fun minimize(action: Action?): ShadowSpec + + fun dependencies(action: Action?): ShadowSpec + + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun transform(clazz: Class): ShadowSpec + + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun transform(clazz: Class, action: Action?): ShadowSpec + + fun transform(transformer: Transformer): ShadowSpec + + fun mergeServiceFiles(): ShadowSpec + + fun mergeServiceFiles(rootPath: String): ShadowSpec + + fun mergeServiceFiles(action: Action?): ShadowSpec + + fun mergeGroovyExtensionModules(): ShadowSpec + + fun append(resourcePath: String): ShadowSpec + + fun relocate(pattern: String, destination: String): ShadowSpec + + fun relocate(pattern: String, destination: String, action: Action?): ShadowSpec + + fun relocate(relocator: Relocator): ShadowSpec + + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun relocate(clazz: Class): ShadowSpec + + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun relocate(clazz: Class, action: Action?): ShadowSpec + + val stats: ShadowStats +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt new file mode 100644 index 000000000..6bea5011c --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt @@ -0,0 +1,10 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +/** + * Marks that a given instance of [Transformer] is compatible with the Gradle build cache. + * In other words, it has its appropriate inputs annotated so that Gradle can consider them when + * determining the cache key. + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +annotation class CacheableTransformer diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt new file mode 100644 index 000000000..9a2aa5e5c --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -0,0 +1,33 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.Named +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Internal + +/** + * Modified from `org.apache.maven.plugins.shade.resource.ResourceTransformer.java` + * + * @author Jason van Zyl + * @author Charlie Knudsen + * @author John Engelman + */ +interface Transformer : Named { + fun canTransformResource(element: FileTreeElement): Boolean + + fun transform(context: TransformerContext) + + fun hasTransformedResource(): Boolean + + fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) + + @Internal + override fun getName(): String = this::class.java.simpleName +} + +object NoOpTransformer : Transformer { + override fun canTransformResource(element: FileTreeElement): Boolean = false + override fun transform(context: TransformerContext): Unit = Unit + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean): Unit = Unit + override fun hasTransformedResource(): Boolean = false +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.groovy deleted file mode 100644 index ccb64cf5c..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.groovy +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.tasks.KnowsTask -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.util.GradleVersion - -class ShadowBasePlugin implements Plugin { - - public static final String EXTENSION_NAME = 'shadow' - public static final String CONFIGURATION_NAME = 'shadow' - public static final String COMPONENT_NAME = 'shadow' - - @Override - void apply(Project project) { - if (GradleVersion.current() < GradleVersion.version("8.3")) { - throw new GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.") - } - project.extensions.create(EXTENSION_NAME, ShadowExtension, project) - createShadowConfiguration(project) - - project.tasks.register(KnowsTask.NAME, KnowsTask) { knows -> - knows.group = ShadowJavaPlugin.SHADOW_GROUP - knows.description = KnowsTask.DESC - } - } - - private static void createShadowConfiguration(Project project) { - project.configurations.create(CONFIGURATION_NAME) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.groovy deleted file mode 100644 index 53fc8b38d..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.groovy +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import org.gradle.api.Project -import org.gradle.api.component.SoftwareComponentContainer -import org.gradle.api.publish.maven.MavenPublication - -@Deprecated -class ShadowExtension { - private final SoftwareComponentContainer components - - ShadowExtension(Project project) { - components = project.components - } - - /** - * @deprecated configure publication using component.shadow directly. - */ - @Deprecated - void component(MavenPublication publication) { - publication.from(components.findByName("shadow")) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.groovy deleted file mode 100644 index 63db72aab..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.groovy +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.plugins.ApplicationPlugin -import org.gradle.api.plugins.JavaPlugin - -class ShadowPlugin implements Plugin { - - @Override - void apply(Project project) { - project.with { - plugins.apply(ShadowBasePlugin) - plugins.withType(JavaPlugin) { - plugins.apply(ShadowJavaPlugin) - } - plugins.withType(ApplicationPlugin) { - plugins.apply(ShadowApplicationPlugin) - } - // 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 and - // etc. if the user applies shadow before those plugins. However, this is fine, because this was also - // the behavior with the old plugin when applying in that order. - plugins.apply(LegacyShadowPlugin) - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy index 3232a9af9..160d34c15 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy @@ -12,8 +12,8 @@ import org.gradle.api.specs.Specs abstract class AbstractDependencyFilter implements DependencyFilter { private final Project project - protected final List> includeSpecs = [] - protected final List> excludeSpecs = [] + protected final List> includeSpecs = [] + protected final List> excludeSpecs = [] AbstractDependencyFilter(Project project) { assert project @@ -35,7 +35,7 @@ abstract class AbstractDependencyFilter implements DependencyFilter { } @Override - FileCollection resolve(Collection configurations) { + FileCollection resolve(Collection configurations) { configurations.collect { resolve(it) }.sum() as FileCollection ?: project.files() @@ -48,7 +48,7 @@ abstract class AbstractDependencyFilter implements DependencyFilter { * @return */ @Override - DependencyFilter exclude(Spec spec) { + DependencyFilter exclude(Spec spec) { excludeSpecs << spec return this } @@ -60,7 +60,7 @@ abstract class AbstractDependencyFilter implements DependencyFilter { * @return */ @Override - DependencyFilter include(Spec spec) { + DependencyFilter include(Spec spec) { includeSpecs << spec return this } @@ -71,7 +71,7 @@ abstract class AbstractDependencyFilter implements DependencyFilter { * @return */ @Override - Spec project(Map notation) { + Spec project(Map notation) { dependency(project.dependencies.project(notation)) } @@ -82,7 +82,7 @@ abstract class AbstractDependencyFilter implements DependencyFilter { * @return */ @Override - Spec project(String notation) { + Spec project(String notation) { dependency(project.dependencies.project(path: notation, configuration: 'default')) } @@ -92,7 +92,7 @@ abstract class AbstractDependencyFilter implements DependencyFilter { * @return */ @Override - Spec dependency(Object notation) { + Spec dependency(Object notation) { dependency(project.dependencies.create(notation)) } @@ -102,7 +102,7 @@ abstract class AbstractDependencyFilter implements DependencyFilter { * @return */ @Override - Spec dependency(Dependency dependency) { + Spec dependency(Dependency dependency) { this.dependency({ ResolvedDependency it -> (!dependency.group || it.moduleGroup.matches(dependency.group)) && (!dependency.name || it.moduleName.matches(dependency.name)) && @@ -116,7 +116,7 @@ abstract class AbstractDependencyFilter implements DependencyFilter { * @return */ @Override - Spec dependency(Closure spec) { + Spec dependency(Closure spec) { return Specs.convertClosureToSpec(spec) } diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.groovy deleted file mode 100644 index e8f6d4644..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.groovy +++ /dev/null @@ -1,75 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ResolvedDependency -import org.gradle.api.file.FileCollection -import org.gradle.api.specs.Spec - -interface DependencyFilter { - - /** - * Resolve a FileCollection against the include/exclude rules in the filter - * @param configuration - * @return - */ - FileCollection resolve(FileCollection configuration) - - /** - * Resolve all FileCollections against the include/exclude ruels in the filter and combine the results - * @param configurations - * @return - */ - FileCollection resolve(Collection configurations) - - /** - * Exclude dependencies that match the provided spec. - * - * @param spec - * @return - */ - DependencyFilter exclude(Spec spec) - - /** - * Include dependencies that match the provided spec. - * - * @param spec - * @return - */ - DependencyFilter include(Spec spec) - - /** - * Create a spec that matches the provided project notation on group, name, and version - * @param notation - * @return - */ - Spec project(Map notation) - - /** - * Create a spec that matches the default configuration for the provided project path on group, name, and version - * - * @param notation - * @return - */ - Spec project(String notation) - - /** - * Create a spec that matches dependencies using the provided notation on group, name, and version - * @param notation - * @return - */ - Spec dependency(Object notation) - - /** - * Create a spec that matches the provided dependency on group, name, and version - * @param dependency - * @return - */ - Spec dependency(Dependency dependency) - - /** - * Create a spec that matches the provided closure - * @param spec - * @return - */ - Spec dependency(Closure spec) -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.groovy deleted file mode 100644 index 966e3f524..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -import java.lang.annotation.ElementType -import java.lang.annotation.Retention -import java.lang.annotation.RetentionPolicy -import java.lang.annotation.Target - -/** - * Marks that a given instance of {@link Relocator} is is compatible with the Gradle build cache. - * In other words, it has its appropriate inputs annotated so that Gradle can consider them when - * determining the cache key. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@interface CacheableRelocator { - -} \ No newline at end of file diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.groovy deleted file mode 100644 index 9e0e97e6f..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.groovy +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import groovy.transform.Canonical -import groovy.transform.builder.Builder - -@Canonical -@Builder -class RelocateClassContext { - - String className - ShadowStats stats - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.groovy deleted file mode 100644 index 34be58b4f..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import groovy.transform.Canonical -import groovy.transform.builder.Builder - -@Canonical -@Builder -class RelocatePathContext { - - String path - ShadowStats stats -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.groovy deleted file mode 100644 index d9cb1d48f..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.groovy +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.relocation - -/** - * Modified from org.apache.maven.plugins.shade.relocation.Relocator.java - * - * @author Jason van Zyl - * @author John Engelman - */ -interface Relocator { - String ROLE = Relocator.class.getName() - - boolean canRelocatePath(String path) - - String relocatePath(RelocatePathContext context) - - boolean canRelocateClass(String className) - - String relocateClass(RelocateClassContext context) - - String applyToSourceContent(String sourceContent) -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy index 7f3ad7d37..04992fb4f 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy @@ -9,6 +9,8 @@ import org.gradle.api.java.archives.ManifestException import org.gradle.api.java.archives.ManifestMergeSpec import org.gradle.api.java.archives.internal.DefaultManifest import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec +import org.jetbrains.annotations.NotNull +import org.jetbrains.annotations.Nullable class DefaultInheritManifest implements InheritManifest { @@ -33,11 +35,11 @@ class DefaultInheritManifest implements InheritManifest { } @Override - InheritManifest inheritFrom(Object inheritPaths, Closure closure) { + InheritManifest inheritFrom(@NotNull Object[] inheritPaths, @Nullable Action action) { DefaultManifestMergeSpec mergeSpec = new DefaultManifestMergeSpec() mergeSpec.from(inheritPaths) inheritMergeSpecs.add(mergeSpec) - project.configure(mergeSpec, closure) + action?.execute(mergeSpec) return this } diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.groovy deleted file mode 100644 index 1cfed3c4c..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.groovy +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import org.gradle.api.java.archives.Manifest - -interface InheritManifest extends Manifest { - - InheritManifest inheritFrom(Object... inheritPaths) - - InheritManifest inheritFrom(inheritPaths, Closure closure) -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java index dc4cfe9e3..8f9b486fe 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java @@ -201,7 +201,7 @@ public ShadowJar dependencies(Action c) { * @return this */ @Override - public ShadowJar transform(Class clazz) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public ShadowJar transform(Class clazz) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { return transform(clazz, null); } @@ -251,7 +251,7 @@ private void addTransform(T transformer, Action c) { @Override public ShadowJar mergeServiceFiles() { try { - transform(ServiceFileTransformer.class); + transform(ServiceFileTransformer.class, null); } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException ignored) { } @@ -294,9 +294,9 @@ public ShadowJar mergeServiceFiles(Action configureClosu * @return this */ @Override - public ShadowJar mergeGroovyExtensionModules() { + public ShadowSpec mergeGroovyExtensionModules() { try { - transform(GroovyExtensionModuleTransformer.class); + transform(GroovyExtensionModuleTransformer.class, null); } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | InstantiationException ignored) { } @@ -364,7 +364,7 @@ public ShadowJar relocate(Relocator relocator) { * @return this */ @Override - public ShadowJar relocate(Class relocatorClass) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + public ShadowJar relocate(Class relocatorClass) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { return relocate(relocatorClass, null); } diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.java b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.java deleted file mode 100644 index cc86e4e5c..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks; - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats; -import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter; -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator; -import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator; -import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer; -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer; -import org.gradle.api.Action; -import org.gradle.api.file.CopySpec; - -import java.lang.reflect.InvocationTargetException; - -interface ShadowSpec extends CopySpec { - ShadowSpec minimize(); - - ShadowSpec minimize(Action configureClosure); - - ShadowSpec dependencies(Action configure); - - ShadowSpec transform(Class clazz) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException; - - ShadowSpec transform(Class clazz, Action configure) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException; - - ShadowSpec transform(Transformer transformer); - - ShadowSpec mergeServiceFiles(); - - ShadowSpec mergeServiceFiles(String rootPath); - - ShadowSpec mergeServiceFiles(Action configureClosure); - - ShadowSpec mergeGroovyExtensionModules(); - - ShadowSpec append(String resourcePath); - - ShadowSpec relocate(String pattern, String destination); - - ShadowSpec relocate(String pattern, String destination, Action configure); - - ShadowSpec relocate(Relocator relocator); - - ShadowSpec relocate(Class clazz) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException; - - ShadowSpec relocate(Class clazz, Action configure) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException; - - ShadowStats getStats(); -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.groovy deleted file mode 100644 index ae3b74e56..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import java.lang.annotation.ElementType -import java.lang.annotation.Retention -import java.lang.annotation.RetentionPolicy -import java.lang.annotation.Target - -/** - * Marks that a given instance of {@link Transformer} is is compatible with the Gradle build cache. - * In other words, it has its appropriate inputs annotated so that Gradle can consider them when - * determining the cache key. - */ -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -@interface CacheableTransformer { - -} \ No newline at end of file diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.groovy deleted file mode 100644 index 630d41301..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.groovy +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.Named -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Internal - -/** - * Modified from org.apache.maven.plugins.shade.resource.ResourceTransformer.java - * - * @author Jason van Zyl - * @author Charlie Knudsen - * @author John Engelman - */ -trait Transformer implements Named { - - abstract boolean canTransformResource(FileTreeElement element) - - abstract void transform(TransformerContext context) - - abstract boolean hasTransformedResource() - - abstract void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) - - @Internal - String getName() { - return getClass().simpleName - } -} From 1f252692b3a196c64ec233048f2afc8fcf456686 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 18 Nov 2024 06:51:25 -0500 Subject: [PATCH 004/941] Kotlin migration part 2 (#1019) * Convert KnowsTask * Convert DefaultInheritManifest * Convert ShadowJavaPlugin * Convert JavaJarExec * Convert ShadowApplicationPlugin --- .../plugins/shadow/ShadowApplicationPlugin.kt | 135 +++++++++++++++++ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 111 ++++++++++++++ .../plugins/shadow/internal/JavaJarExec.kt | 22 +++ .../gradle/plugins/shadow/internal/Utils.kt | 11 ++ .../shadow/tasks/DefaultInheritManifest.kt | 45 ++++++ .../gradle/plugins/shadow/tasks/KnowsTask.kt | 24 +++ .../shadow/ShadowApplicationPlugin.groovy | 139 ------------------ .../plugins/shadow/ShadowJavaPlugin.groovy | 108 -------------- .../shadow/internal/JavaJarExec.groovy | 19 --- .../tasks/DefaultInheritManifest.groovy | 106 ------------- .../plugins/shadow/tasks/KnowsTask.groovy | 17 --- .../plugins/shadow/tasks/ShadowJar.java | 2 +- 12 files changed, 349 insertions(+), 390 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.groovy diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt new file mode 100644 index 000000000..a6b17a2c4 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -0,0 +1,135 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.internal.JavaJarExec +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import org.gradle.api.GradleException +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.distribution.DistributionContainer +import org.gradle.api.plugins.ApplicationPlugin +import org.gradle.api.plugins.JavaApplication +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.Sync +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.application.CreateStartScripts +import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator +import org.gradle.jvm.toolchain.JavaToolchainService + +abstract class ShadowApplicationPlugin : Plugin { + private lateinit var project: Project + private lateinit var javaApplication: JavaApplication + + override fun apply(project: Project) { + this.project = project + this.javaApplication = project.extensions.getByType(JavaApplication::class.java) + + addRunTask() + addCreateScriptsTask() + configureDistSpec() + configureJarMainClass() + configureInstallTask() + } + + protected open fun configureJarMainClass() { + val classNameProvider = javaApplication.mainClass + shadowJar.configure { jar -> + jar.inputs.property("mainClassName", classNameProvider) + jar.doFirst { + jar.manifest.attributes["Main-Class"] = classNameProvider.get() + } + } + } + + protected open fun addRunTask() { + project.tasks.register(SHADOW_RUN_TASK_NAME, JavaJarExec::class.java) { + val install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) + it.dependsOn(install) + it.mainClass.set("-jar") + it.description = "Runs this project as a JVM application using the shadow jar" + it.group = ApplicationPlugin.APPLICATION_GROUP + it.conventionMapping.map("jvmArgs") { javaApplication.applicationDefaultJvmArgs } + it.jarFile.fileProvider( + project.providers.provider { + project.file("${install.get().destinationDir.path}/lib/${shadowJar.get().archiveFile.get().asFile.name}") + }, + ) + val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain + val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java) + .launcherFor(toolchain) + it.javaLauncher.set(defaultLauncher) + } + } + + protected open fun addCreateScriptsTask() { + project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { + (it.unixStartScriptGenerator as TemplateBasedScriptGenerator).template = + project.resources.text.fromString(this::class.java.requireResourceAsText("internal/unixStartScript.txt")) + (it.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = + project.resources.text.fromString(this::class.java.requireResourceAsText("internal/windowsStartScript.txt")) + it.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" + it.group = ApplicationPlugin.APPLICATION_GROUP + it.classpath = project.files(shadowJar) + it.conventionMapping.map("mainClassName") { javaApplication.mainClass.get() } + it.conventionMapping.map("applicationName") { javaApplication.applicationName } + it.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile } + it.conventionMapping.map("defaultJvmOpts") { javaApplication.applicationDefaultJvmArgs } + it.inputs.files(project.files(shadowJar)) + } + } + + protected open fun configureInstallTask() { + project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java).configure { task -> + val applicationName = project.providers.provider { javaApplication.applicationName } + + task.doFirst { + if ( + !task.destinationDir.listFiles().isNullOrEmpty() && + ( + !task.destinationDir.resolve("lib").isDirectory || + !task.destinationDir.resolve("bin").isDirectory + ) + ) { + throw GradleException( + "The specified installation directory '${task.destinationDir}' is neither empty nor does it contain an installation for '${applicationName.get()}'.\n" + + "If you really want to install to this directory, delete it and run the install task again.\n" + + "Alternatively, choose a different installation directory.", + ) + } + } + task.doLast { + task.eachFile { + if (it.path == "bin/${applicationName.get()}") { + it.mode = 0x755 + } + } + } + } + } + + protected open fun configureDistSpec() { + project.extensions.getByType(DistributionContainer::class.java) + .register(ShadowBasePlugin.DISTRIBUTION_NAME) { distributions -> + distributions.contents { contents -> + contents.from(project.file("src/dist")) + contents.into("lib") { lib -> + lib.from(shadowJar) + lib.from(project.configurations.named(ShadowBasePlugin.CONFIGURATION_NAME)) + } + contents.into("bin") { bin -> + bin.from(project.tasks.named(SHADOW_SCRIPTS_TASK_NAME)) + bin.filePermissions { it.unix(493) } + } + } + } + } + + protected val shadowJar: TaskProvider + get() = project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar::class.java) + + companion object { + const val SHADOW_RUN_TASK_NAME: String = "runShadow" + const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" + const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt new file mode 100644 index 000000000..5eb6fecd5 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -0,0 +1,111 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import javax.inject.Inject +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.Bundling +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.LibraryElements +import org.gradle.api.attributes.Usage +import org.gradle.api.component.AdhocComponentWithVariants +import org.gradle.api.component.SoftwareComponentFactory +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.jvm.tasks.Jar +import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin + +abstract class ShadowJavaPlugin @Inject constructor( + private val softwareComponentFactory: SoftwareComponentFactory, +) : Plugin { + + override fun apply(project: Project) { + val shadowConfiguration = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) + val shadowTaskProvider = configureShadowTask(project, shadowConfiguration) + + project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { + it.extendsFrom(shadowConfiguration) + } + + val shadowRuntimeElements = project.configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { + it.extendsFrom(shadowConfiguration) + it.isCanBeConsumed = true + it.isCanBeResolved = false + it.attributes { attr -> + attr.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) + attr.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category::class.java, Category.LIBRARY)) + attr.attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + project.objects.named(LibraryElements::class.java, LibraryElements.JAR), + ) + attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling::class.java, Bundling.SHADOWED)) + } + it.outgoing.artifact(shadowTaskProvider) + } + + project.components.named("java", AdhocComponentWithVariants::class.java) { + it.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> + variant.mapToOptional() + } + } + + val shadowComponent = softwareComponentFactory.adhoc(ShadowBasePlugin.COMPONENT_NAME) + project.components.add(shadowComponent) + shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> + variant.mapToMavenScope("runtime") + } + + project.plugins.withType(JavaGradlePluginPlugin::class.java).configureEach { + // Remove the gradleApi so it isn't merged into the jar file. + // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. + // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/de + project.configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { + it.dependencies.remove(project.dependencies.gradleApi()) + } + // Compile only gradleApi() to make sure the plugin can compile against Gradle API. + project.configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) { + it.dependencies.add(project.dependencies.gradleApi()) + } + } + } + + private fun configureShadowTask(project: Project, shadowConfiguration: Configuration): TaskProvider { + val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) + val jarTask = project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar::class.java) + val taskProvider = project.tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { shadow -> + shadow.group = ShadowBasePlugin.GROUP_NAME + shadow.description = "Create a combined JAR of project and runtime dependencies" + shadow.archiveClassifier.set("all") + shadow.manifest.inheritFrom(jarTask.get().manifest) + val attrProvider = jarTask.map { it.manifest.attributes["Class-Path"]?.toString().orEmpty() } + val files = project.objects.fileCollection().from(shadowConfiguration) + shadow.doFirst { + if (!files.isEmpty) { + val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } + shadow.manifest.attributes["Class-Path"] = attrs.joinToString(" ").trim() + } + } + shadow.from(sourceSets.getByName("main").output) + shadow.configurations = listOf( + project.configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) + ?: project.configurations.getByName("runtime"), + ) + shadow.exclude( + "META-INF/INDEX.LIST", + "META-INF/*.SF", + "META-INF/*.DSA", + "META-INF/*.RSA", + "module-info.class", + ) + } + project.artifacts.add(shadowConfiguration.name, taskProvider) + return taskProvider + } + + companion object { + const val SHADOW_JAR_TASK_NAME: String = "shadowJar" + const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt new file mode 100644 index 000000000..316320d82 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt @@ -0,0 +1,22 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.JavaExec +import org.gradle.api.tasks.TaskAction + +internal abstract class JavaJarExec : JavaExec() { + @get:InputFile + abstract val jarFile: RegularFileProperty + + @TaskAction + override fun exec() { + val allArgs = buildList { + add(jarFile.get().asFile.path) + // Must cast args to List here to avoid type mismatch. + addAll(args as List) + } + setArgs(allArgs) + super.exec() + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt new file mode 100644 index 000000000..38179d9d1 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -0,0 +1,11 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import java.io.InputStream + +internal fun Class<*>.requireResourceAsText(name: String): String { + return requireResourceAsStream(name).bufferedReader().readText() +} + +private fun Class<*>.requireResourceAsStream(name: String): InputStream { + return getResourceAsStream(name) ?: error("Resource $name not found.") +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt new file mode 100644 index 000000000..0a16a6037 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt @@ -0,0 +1,45 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import org.gradle.api.Action +import org.gradle.api.internal.file.FileResolver +import org.gradle.api.java.archives.Manifest +import org.gradle.api.java.archives.internal.DefaultManifest +import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec + +open class DefaultInheritManifest @JvmOverloads constructor( + private val fileResolver: FileResolver, + private val internalManifest: DefaultManifest = DefaultManifest(fileResolver), +) : InheritManifest, + Manifest by internalManifest { + private val inheritMergeSpecs = mutableListOf() + + override fun inheritFrom( + vararg inheritPaths: Any, + ): InheritManifest { + return inheritFrom(inheritPaths = inheritPaths, action = null) + } + + override fun inheritFrom( + vararg inheritPaths: Any, + action: Action<*>?, + ): InheritManifest = apply { + val mergeSpec = DefaultManifestMergeSpec() + mergeSpec.from(*inheritPaths) + inheritMergeSpecs.add(mergeSpec) + @Suppress("UNCHECKED_CAST") + (action as? Action)?.execute(mergeSpec) + } + + override fun getEffectiveManifest(): DefaultManifest { + var base = DefaultManifest(fileResolver) + inheritMergeSpecs.forEach { + base = it.merge(base, fileResolver) + } + base.from(internalManifest) + return base.effectiveManifest + } + + override fun writeTo(path: Any): Manifest = apply { + effectiveManifest.writeTo(path) + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt new file mode 100644 index 000000000..35cc81b74 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt @@ -0,0 +1,24 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText +import org.gradle.api.DefaultTask +import org.gradle.api.tasks.TaskAction + +abstract class KnowsTask : DefaultTask() { + + @TaskAction + fun knows() { + logger.info( + """ + No, The Shadow Knows.... + + ${this::class.java.requireResourceAsText("/shadowBanner.txt")} + """.trimIndent(), + ) + } + + companion object { + const val NAME: String = "knows" + const val DESC: String = "Do you know who knows?" + } +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.groovy deleted file mode 100644 index 7a57952ae..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.groovy +++ /dev/null @@ -1,139 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.internal.JavaJarExec -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.gradle.api.GradleException -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.distribution.Distribution -import org.gradle.api.distribution.DistributionContainer -import org.gradle.api.file.CopySpec -import org.gradle.api.plugins.ApplicationPlugin -import org.gradle.api.plugins.JavaApplication -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.provider.Provider -import org.gradle.api.tasks.Sync -import org.gradle.api.tasks.TaskProvider -import org.gradle.api.tasks.application.CreateStartScripts -import org.gradle.jvm.toolchain.JavaLauncher -import org.gradle.jvm.toolchain.JavaToolchainService - -class ShadowApplicationPlugin implements Plugin { - - public static final String SHADOW_RUN_TASK_NAME = 'runShadow' - public static final String SHADOW_SCRIPTS_TASK_NAME = 'startShadowScripts' - public static final String SHADOW_INSTALL_TASK_NAME = 'installShadowDist' - - private Project project - private JavaApplication javaApplication - - @Override - void apply(Project project) { - this.project = project - this.javaApplication = project.extensions.getByType(JavaApplication) - - DistributionContainer distributions = project.extensions.getByName("distributions") as DistributionContainer - Distribution distribution = distributions.create("shadow") - - addRunTask(project) - addCreateScriptsTask(project) - - configureDistSpec(project, distribution.contents) - - configureJarMainClass(project) - configureInstallTask(project) - } - - protected void configureJarMainClass(Project project) { - def classNameProvider = javaApplication.mainClass - jar.configure { jar -> - jar.inputs.property('mainClassName', classNameProvider) - jar.doFirst { - jar.manifest.attributes 'Main-Class': classNameProvider.get() - } - } - } - - protected void addRunTask(Project project) { - - project.tasks.register(SHADOW_RUN_TASK_NAME, JavaJarExec) { run -> - def install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync) - run.dependsOn SHADOW_INSTALL_TASK_NAME - run.mainClass.set('-jar') - run.description = 'Runs this project as a JVM application using the shadow jar' - run.group = ApplicationPlugin.APPLICATION_GROUP - run.conventionMapping.jvmArgs = { javaApplication.applicationDefaultJvmArgs } - run.conventionMapping.jarFile = { - project.file("${install.get().destinationDir.path}/lib/${jar.get().archiveFile.get().asFile.name}") - } - configureJavaLauncher(run) - } - } - - private void configureJavaLauncher(JavaJarExec run) { - def toolchain = project.getExtensions().getByType(JavaPluginExtension.class).toolchain - JavaToolchainService service = project.getExtensions().getByType(JavaToolchainService.class) - Provider defaultLauncher = service.launcherFor(toolchain) - run.getJavaLauncher().set(defaultLauncher) - } - - protected void addCreateScriptsTask(Project project) { - project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts) { startScripts -> - startScripts.unixStartScriptGenerator.template = project.resources.text.fromString(this.class.getResource("internal/unixStartScript.txt").text) - startScripts.windowsStartScriptGenerator.template = project.resources.text.fromString(this.class.getResource("internal/windowsStartScript.txt").text) - startScripts.description = 'Creates OS specific scripts to run the project as a JVM application using the shadow jar' - startScripts.group = ApplicationPlugin.APPLICATION_GROUP - startScripts.classpath = project.files(jar) - startScripts.conventionMapping.mainClassName = { javaApplication.mainClass.get() } - startScripts.conventionMapping.applicationName = { javaApplication.applicationName } - startScripts.conventionMapping.outputDir = { new File(project.layout.buildDirectory.asFile.get(), 'scriptsShadow') } - startScripts.conventionMapping.defaultJvmOpts = { javaApplication.applicationDefaultJvmArgs } - startScripts.inputs.files project.objects.fileCollection().from { -> jar } - } - } - - protected void configureInstallTask(Project project) { - project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync).configure { task -> - task.doFirst { - if (task.destinationDir.directory) { - if (task.destinationDir.listFiles().size() != 0 && (!new File(task.destinationDir, 'lib').directory || !new File(task.destinationDir, 'bin').directory)) { - throw new GradleException("The specified installation directory '${task.destinationDir}' is neither empty nor does it contain an installation for '${javaApplication.applicationName}'.\n" + - "If you really want to install to this directory, delete it and run the install task again.\n" + - "Alternatively, choose a different installation directory." - ) - } - } - } - task.doLast { - task.eachFile { - if (it.path == "bin/${javaApplication.applicationName}") { - it.mode = 0x755 - } - } - } - } - } - - protected CopySpec configureDistSpec(Project project, CopySpec distSpec) { - def startScripts = project.tasks.named(SHADOW_SCRIPTS_TASK_NAME) - - distSpec.with { - from(project.file("src/dist")) - - into("lib") { - from(jar) - from(project.configurations.shadow) - } - into("bin") { - from(startScripts) - filePermissions { it.unix(493) } - } - } - - distSpec - } - - private TaskProvider getJar() { - project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy deleted file mode 100644 index 7a904a218..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.groovy +++ /dev/null @@ -1,108 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.gradle.api.Plugin -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.attributes.Bundling -import org.gradle.api.attributes.Category -import org.gradle.api.attributes.LibraryElements -import org.gradle.api.attributes.Usage -import org.gradle.api.component.AdhocComponentWithVariants -import org.gradle.api.component.SoftwareComponentFactory -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.tasks.SourceSetContainer -import org.gradle.api.tasks.TaskProvider -import org.gradle.jvm.tasks.Jar -import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin - -import javax.inject.Inject - -class ShadowJavaPlugin implements Plugin { - - public static final String SHADOW_JAR_TASK_NAME = 'shadowJar' - public static final String SHADOW_GROUP = 'Shadow' - public static final String SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME = 'shadowRuntimeElements' - - private final SoftwareComponentFactory softwareComponentFactory - - @Inject - ShadowJavaPlugin(SoftwareComponentFactory softwareComponentFactory) { - this.softwareComponentFactory = softwareComponentFactory - } - - @Override - void apply(Project project) { - def shadowConfiguration = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) - def shadowTaskProvider = configureShadowTask(project, shadowConfiguration) - - project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { - it.extendsFrom(shadowConfiguration) - } - - def shadowRuntimeElements = project.configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { Configuration it -> - it.extendsFrom(shadowConfiguration) - it.canBeConsumed = true - it.canBeResolved = false - it.attributes { - it.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage, Usage.JAVA_RUNTIME)) - it.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category, Category.LIBRARY)) - it.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, LibraryElements.JAR)) - it.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling, Bundling.SHADOWED)) - } - it.outgoing.artifact(shadowTaskProvider) - } - - project.components.named("java", AdhocComponentWithVariants) { - it.addVariantsFromConfiguration(shadowRuntimeElements) { - it.mapToOptional() - } - } - - AdhocComponentWithVariants shadowComponent = softwareComponentFactory.adhoc(ShadowBasePlugin.COMPONENT_NAME) - project.components.add(shadowComponent) - shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { - it.mapToMavenScope("runtime") - } - - project.plugins.withType(JavaGradlePluginPlugin).configureEach { - // Remove the gradleApi so it isn't merged into the jar file. - // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. - // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161 - project.configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { - it.dependencies.remove(project.dependencies.gradleApi()) - } - // Compile only gradleApi() to make sure the plugin can compile against Gradle API. - project.configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) { - it.dependencies.add(project.dependencies.gradleApi()) - } - } - } - - protected static TaskProvider configureShadowTask(Project project, Configuration shadowConfiguration) { - SourceSetContainer sourceSets = project.extensions.getByType(SourceSetContainer) - def jarTask = project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar) - def taskProvider = project.tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar) { shadow -> - shadow.group = SHADOW_GROUP - shadow.description = 'Create a combined JAR of project and runtime dependencies' - shadow.archiveClassifier.set("all") - shadow.manifest.inheritFrom(jarTask.get().manifest) - def attrProvider = jarTask.map { it.manifest.attributes.get('Class-Path') } - def files = project.objects.fileCollection().from(shadowConfiguration) - shadow.doFirst { - if (!files.empty) { - def attrs = [attrProvider.getOrElse('')] + files.collect { it.name } - shadow.manifest.attributes 'Class-Path': attrs.join(' ').trim() - } - } - shadow.from(sourceSets.main.output) - shadow.configurations = [ - project.configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) ?: - project.configurations.runtime, - ] - shadow.exclude('META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'module-info.class') - } - project.artifacts.add(shadowConfiguration.name, taskProvider) - return taskProvider - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.groovy deleted file mode 100644 index 2affa9fbe..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.groovy +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.JavaExec -import org.gradle.api.tasks.TaskAction - -class JavaJarExec extends JavaExec { - - @InputFile - File jarFile - - @Override - @TaskAction - void exec() { - List allArgs = [getJarFile().path] + getArgs() - setArgs(allArgs) - super.exec() - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy deleted file mode 100644 index 04992fb4f..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.groovy +++ /dev/null @@ -1,106 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import org.gradle.api.Action -import org.gradle.api.Project -import org.gradle.api.internal.file.FileResolver -import org.gradle.api.java.archives.Attributes -import org.gradle.api.java.archives.Manifest -import org.gradle.api.java.archives.ManifestException -import org.gradle.api.java.archives.ManifestMergeSpec -import org.gradle.api.java.archives.internal.DefaultManifest -import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec -import org.jetbrains.annotations.NotNull -import org.jetbrains.annotations.Nullable - -class DefaultInheritManifest implements InheritManifest { - - private List inheritMergeSpecs = [] - - private final transient Project project - - private final FileResolver fileResolver - - private final Manifest internalManifest - - DefaultInheritManifest(Project project, FileResolver fileResolver) { - this.project = project - this.internalManifest = new DefaultManifest(fileResolver) - this.fileResolver = fileResolver - } - - @Override - InheritManifest inheritFrom(Object... inheritPaths) { - inheritFrom(inheritPaths, null) - return this - } - - @Override - InheritManifest inheritFrom(@NotNull Object[] inheritPaths, @Nullable Action action) { - DefaultManifestMergeSpec mergeSpec = new DefaultManifestMergeSpec() - mergeSpec.from(inheritPaths) - inheritMergeSpecs.add(mergeSpec) - action?.execute(mergeSpec) - return this - } - - @Override - Attributes getAttributes() { - return internalManifest.getAttributes() - } - - @Override - Map getSections() { - return internalManifest.getSections() - } - - @Override - Manifest attributes(Map map) throws ManifestException { - internalManifest.attributes(map) - return this - } - - @Override - Manifest attributes(Map map, String s) throws ManifestException { - internalManifest.attributes(map, s) - return this - } - - @Override - DefaultManifest getEffectiveManifest() { - DefaultManifest base = new DefaultManifest(fileResolver) - inheritMergeSpecs.each { - base = it.merge(base, fileResolver) - } - base.from internalManifest - return base.getEffectiveManifest() - } - - Manifest writeTo(Writer writer) { - this.getEffectiveManifest().writeTo((Object) writer) - return this - } - - @Override - Manifest writeTo(Object o) { - this.getEffectiveManifest().writeTo(o) - return this - } - - @Override - Manifest from(Object... objects) { - internalManifest.from(objects) - return this - } - - @Override - Manifest from(Object o, Closure closure) { - internalManifest.from(o, closure) - return this - } - - @Override - Manifest from(Object o, Action action) { - internalManifest.from(o, action) - return this - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.groovy deleted file mode 100644 index 4a103ffe0..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.groovy +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import org.codehaus.groovy.reflection.ReflectionUtils -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.TaskAction - -class KnowsTask extends DefaultTask { - - public static final String NAME = "knows" - public static final String DESC = "Do you know who knows?" - - @TaskAction - def knows() { - println "\nNo, The Shadow Knows...." - println ReflectionUtils.getCallingClass(0).getResourceAsStream("/shadowBanner.txt").text - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java index 8f9b486fe..bae7aa6ed 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java @@ -54,7 +54,7 @@ public ShadowJar() { setDuplicatesStrategy(DuplicatesStrategy.INCLUDE); //shadow filters out files later. This was the default behavior in Gradle < 6.x dependencyFilter = new DefaultDependencyFilter(getProject()); dependencyFilterForMinimize = new MinimizeDependencyFilter(getProject()); - setManifest(new DefaultInheritManifest(getProject(), getServices().get(FileResolver.class))); + setManifest(new DefaultInheritManifest(getServices().get(FileResolver.class))); transformers = new ArrayList<>(); relocators = new ArrayList<>(); configurations = new ArrayList<>(); From 48ace22706449bc34791f73f2cf9c4c2257e1298 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 18 Nov 2024 07:49:00 -0500 Subject: [PATCH 005/941] Kotlin migration part 3 (#1020) * Move ZipCompressor into `com.github.jengelman.gradle.plugins.shadow.tasks` * Convert ZipCompressor * Convert CleanProperties * Convert DefaultZipCompressor * Rename TransformerContext.is * Convert TransformerContext * Convert ShadowStats * Convert ApacheLicenseResourceTransformer * Convert DontIncludeResourceTransformer * Convert IncludeResourceTransformer --- .../gradle/plugins/shadow/ShadowStats.kt | 72 +++++++++++++++++ .../shadow/internal/CleanProperties.kt | 28 +++++++ .../shadow/internal/DefaultZipCompressor.kt | 25 ++++++ .../plugins/shadow/tasks/ZipCompressor.kt | 5 ++ .../ApacheLicenseResourceTransformer.kt | 23 ++++++ .../DontIncludeResourceTransformer.kt | 23 ++++++ .../IncludeResourceTransformer.kt | 39 +++++++++ .../shadow/transformers/TransformerContext.kt | 47 +++++++++++ .../gradle/plugins/shadow/ShadowStats.groovy | 81 ------------------- .../shadow/internal/CleanProperties.groovy | 34 -------- .../internal/DefaultZipCompressor.groovy | 44 ---------- .../shadow/internal/GradleVersionUtil.groovy | 1 + .../shadow/internal/ZipCompressor.groovy | 21 ----- .../shadow/tasks/ShadowCopyAction.groovy | 3 +- .../ApacheLicenseResourceTransformer.groovy | 58 ------------- .../ApacheNoticeResourceTransformer.groovy | 4 +- .../transformers/AppendingTransformer.groovy | 4 +- .../ComponentsXmlResourceTransformer.groovy | 4 +- .../DontIncludeResourceTransformer.groovy | 66 --------------- .../GroovyExtensionModuleTransformer.groovy | 2 +- .../IncludeResourceTransformer.groovy | 73 ----------------- .../Log4j2PluginsCacheFileTransformer.groovy | 4 +- .../ManifestAppenderTransformer.groovy | 4 +- .../ManifestResourceTransformer.groovy | 4 +- .../PropertiesFileTransformer.groovy | 2 +- .../ServiceFileTransformer.groovy | 2 +- .../transformers/TransformerContext.groovy | 22 ----- .../XmlAppendingTransformer.groovy | 2 +- ...ceResourceTransformerParameterTests.groovy | 2 +- ...omponentsXmlResourceTransformerTest.groovy | 4 +- ...g4j2PluginsCacheFileTransformerSpec.groovy | 2 +- .../PropertiesFileTransformerTest.groovy | 2 +- .../TransformerSpecSupport.groovy | 4 +- 33 files changed, 287 insertions(+), 424 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowStats.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.groovy diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt new file mode 100644 index 000000000..01cc1b761 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt @@ -0,0 +1,72 @@ +package com.github.jengelman.gradle.plugins.shadow + +import org.gradle.api.GradleException + +open class ShadowStats { + open var totalTime: Long = 0 + open var jarStartTime: Long = 0 + open var jarEndTime: Long = 0 + open var jarCount: Int = 1 + open var processingJar: Boolean = false + open val relocations: MutableMap = mutableMapOf() + + open val relocationString: String + get() { + return relocations.map { (k, v) -> "$k → $v" } + .sorted() + .joinToString("\n") + } + + open val jarTiming: Long + get() = jarEndTime - jarStartTime + + open val totalTimeSecs: Double + get() = totalTime / 1000.0 + + open val averageTimePerJar: Double + get() = totalTime / jarCount.toDouble() + + open val averageTimeSecsPerJar: Double + get() = averageTimePerJar / 1000.0 + + open val buildScanData: Map + get() = mapOf( + "dependencies" to jarCount.toString(), + "relocations" to relocationString, + ) + + open fun relocate(src: String, dst: String) { + relocations[src] = dst + } + + open fun startJar() { + if (processingJar) throw GradleException("Can only time one entry at a time") + processingJar = true + jarStartTime = System.currentTimeMillis() + } + + open fun finishJar() { + if (processingJar) { + jarEndTime = System.currentTimeMillis() + jarCount++ + totalTime += jarTiming + processingJar = false + } + } + + open fun printStats() { + println(this) + } + + override fun toString(): String { + return """ + ******************* + GRADLE SHADOW STATS + + Total Jars: $jarCount (includes project) + Total Time: ${totalTimeSecs}s [${totalTime}ms] + Average Time/Jar: ${averageTimeSecsPerJar}s [${averageTimePerJar}ms] + ******************* + """.trimIndent() + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt new file mode 100644 index 000000000..3f6be3d9b --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt @@ -0,0 +1,28 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import java.io.BufferedWriter +import java.io.IOException +import java.io.Writer +import java.util.Date +import java.util.Properties + +internal class CleanProperties : Properties() { + @Throws(IOException::class) + override fun store(writer: Writer, comments: String) { + super.store(StripCommentsWithTimestampBufferedWriter(writer), comments) + } + + private class StripCommentsWithTimestampBufferedWriter(out: Writer) : BufferedWriter(out) { + private val lengthOfExpectedTimestamp = ("#" + Date().toString()).length + + @Throws(IOException::class) + override fun write(str: String) { + if (str.couldBeCommentWithTimestamp) return + super.write(str) + } + + private val String?.couldBeCommentWithTimestamp: Boolean get() { + return this != null && startsWith("#") && length == lengthOfExpectedTimestamp + } + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt new file mode 100644 index 000000000..244bccd84 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt @@ -0,0 +1,25 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import com.github.jengelman.gradle.plugins.shadow.tasks.ZipCompressor +import java.io.File +import org.apache.tools.zip.Zip64Mode +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.UncheckedIOException + +internal class DefaultZipCompressor( + allowZip64Mode: Boolean, + private val entryCompressionMethod: Int, +) : ZipCompressor { + private val zip64Mode = if (allowZip64Mode) Zip64Mode.AsNeeded else Zip64Mode.Never + + override fun createArchiveOutputStream(destination: File): ZipOutputStream { + try { + return ZipOutputStream(destination).apply { + setUseZip64(zip64Mode) + setMethod(entryCompressionMethod) + } + } catch (e: Exception) { + throw UncheckedIOException("Unable to create ZIP output stream for file $destination.", e) + } + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt new file mode 100644 index 000000000..a032298de --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt @@ -0,0 +1,5 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory + +interface ZipCompressor : ArchiveOutputStreamFactory diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt new file mode 100644 index 000000000..3bd32275a --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -0,0 +1,23 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import org.gradle.api.file.FileTreeElement + +/** + * Prevents duplicate copies of the license + * + * Modified from `org.apache.maven.plugins.shade.resouce.ApacheLicenseResourceTransformer.java` + * + * @author John Engelman + */ +open class ApacheLicenseResourceTransformer : Transformer by NoOpTransformer { + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return LICENSE_PATH.equals(path, ignoreCase = true) || + LICENSE_TXT_PATH.regionMatches(0, path, 0, LICENSE_TXT_PATH.length, ignoreCase = true) + } + + private companion object { + private const val LICENSE_PATH = "META-INF/LICENSE" + private const val LICENSE_TXT_PATH = "META-INF/LICENSE.txt" + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt new file mode 100644 index 000000000..b11ed219b --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -0,0 +1,23 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * A resource processor that prevents the inclusion of an arbitrary resource into the shaded JAR. + * + * Modified from `org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java` + * + * @author John Engelman + */ +open class DontIncludeResourceTransformer : Transformer by NoOpTransformer { + @get:Optional + @get:Input + var resource: String? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return !resource.isNullOrEmpty() && path.endsWith(resource!!) + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt new file mode 100644 index 000000000..53540fc54 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -0,0 +1,39 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp +import java.io.File +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity + +/** + * A resource processor that allows the addition of an arbitrary file + * content into the shaded JAR. + * + * Modified from `org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java` + * + * @author John Engelman + */ +open class IncludeResourceTransformer : Transformer by NoOpTransformer { + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + var file: File? = null + + @get:Input + var resource: String? = null + + override fun hasTransformedResource(): Boolean = file?.exists() == true + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(requireNotNull(resource)) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + + requireNotNull(file).inputStream().use { inputStream -> + inputStream.copyTo(os) + } + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt new file mode 100644 index 000000000..e9d1b6cab --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -0,0 +1,47 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction +import java.io.InputStream +import java.util.GregorianCalendar + +data class TransformerContext @JvmOverloads constructor( + val path: String, + val inputStream: InputStream, + val relocators: List = emptyList(), + val stats: ShadowStats = ShadowStats(), +) { + class Builder { + private var path = "" + private var inputStream: InputStream? = null + private var relocators = emptyList() + private var stats = ShadowStats() + + fun path(path: String): Builder = apply { this.path = path } + fun inputStream(inputStream: InputStream): Builder = apply { this.inputStream = inputStream } + fun relocators(relocators: List): Builder = apply { this.relocators = relocators } + fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + fun build(): TransformerContext = TransformerContext( + path = path, + inputStream = inputStream ?: error("inputStream is required"), + relocators = relocators, + stats = stats, + ) + } + + companion object { + /** + * TODO: replace this with [ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES] + */ + private val CONSTANT_TIME_FOR_ZIP_ENTRIES = GregorianCalendar(1980, 1, 1, 0, 0, 0).getTimeInMillis() + + @JvmStatic + fun builder(): Builder = Builder() + + @JvmStatic + fun getEntryTimestamp(preserveFileTimestamps: Boolean, entryTime: Long): Long { + return if (preserveFileTimestamps) entryTime else CONSTANT_TIME_FOR_ZIP_ENTRIES + } + } +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowStats.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowStats.groovy deleted file mode 100644 index 8813dc61b..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowStats.groovy +++ /dev/null @@ -1,81 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import groovy.util.logging.Slf4j -import org.gradle.api.GradleException - -@Slf4j -class ShadowStats { - - long totalTime - long jarStartTime - long jarEndTime - int jarCount = 1 - boolean processingJar - Map relocations = [:] - - void relocate(String src, String dst) { - relocations[src] = dst - } - - String getRelocationString() { - def maxLength = relocations.keySet().collect { it.length() }.max() - relocations.collect { k, v -> "${k} ${separator(k, maxLength)} ${v}"}.sort().join("\n") - } - - static String separator(String key, int max) { - return "→" - } - - void startJar() { - if (processingJar) throw new GradleException("Can only time one entry at a time") - processingJar = true - jarStartTime = System.currentTimeMillis() - } - - void finishJar() { - if (processingJar) { - jarEndTime = System.currentTimeMillis() - jarCount++ - totalTime += jarTiming - processingJar = false - } - } - - void printStats() { - println this - } - - long getJarTiming() { - jarEndTime - jarStartTime - } - - double getTotalTimeSecs() { - totalTime / 1000 - } - - double getAverageTimePerJar() { - totalTime / jarCount - } - - double getAverageTimeSecsPerJar() { - averageTimePerJar / 1000 - } - - String toString() { - StringBuilder sb = new StringBuilder() - sb.append "*******************\n" - sb.append "GRADLE SHADOW STATS\n" - sb.append "\n" - sb.append "Total Jars: $jarCount (includes project)\n" - sb.append "Total Time: ${totalTimeSecs}s [${totalTime}ms]\n" - sb.append "Average Time/Jar: ${averageTimeSecsPerJar}s [${averageTimePerJar}ms]\n" - sb.append "*******************" - } - - Map getBuildScanData() { - [ - dependencies: jarCount, - relocations : relocationString, - ] as Map - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.groovy deleted file mode 100644 index b54b5271b..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.groovy +++ /dev/null @@ -1,34 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -class CleanProperties extends Properties { - - private static class StripCommentsWithTimestampBufferedWriter extends BufferedWriter { - - private final int lengthOfExpectedTimestamp - - StripCommentsWithTimestampBufferedWriter(final Writer out) { - super(out) - - lengthOfExpectedTimestamp = ("#" + new Date().toString()).length() - } - - @Override - void write(final String str) throws IOException { - if (couldBeCommentWithTimestamp(str)) { - return - } - super.write(str) - } - - private boolean couldBeCommentWithTimestamp(final String str) { - return str != null && - str.startsWith("#") && - str.length() == lengthOfExpectedTimestamp - } - } - - @Override - void store(final Writer writer, final String comments) throws IOException { - super.store(new StripCommentsWithTimestampBufferedWriter(writer), comments) - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.groovy deleted file mode 100644 index a2248e6a2..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.groovy +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2012 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.github.jengelman.gradle.plugins.shadow.internal - -import org.apache.tools.zip.Zip64Mode -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.UncheckedIOException - -class DefaultZipCompressor implements ZipCompressor { - private final int entryCompressionMethod - private final Zip64Mode zip64Mode - - DefaultZipCompressor(boolean allowZip64Mode, int entryCompressionMethod) { - this.entryCompressionMethod = entryCompressionMethod - zip64Mode = allowZip64Mode ? Zip64Mode.AsNeeded : Zip64Mode.Never - } - - @Override - ZipOutputStream createArchiveOutputStream(File destination) { - try { - ZipOutputStream zipOutputStream = new ZipOutputStream(destination) - zipOutputStream.setUseZip64(zip64Mode) - zipOutputStream.setMethod(entryCompressionMethod) - return zipOutputStream - } catch (Exception e) { - String message = String.format("Unable to create ZIP output stream for file %s.", destination) - throw new UncheckedIOException(message, e) - } - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy index 3bc6f0563..ae1a4fff6 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import com.github.jengelman.gradle.plugins.shadow.tasks.ZipCompressor import org.apache.tools.zip.ZipOutputStream import org.gradle.api.internal.file.copy.CopySpecInternal import org.gradle.api.tasks.bundling.Jar diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.groovy deleted file mode 100644 index 86ed6d5e6..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.groovy +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2012 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.github.jengelman.gradle.plugins.shadow.internal - -import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory - -interface ZipCompressor extends ArchiveOutputStreamFactory { -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy index 128dc3cce..7935af4f3 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.impl.RelocatorRemapper import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker -import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext @@ -425,7 +424,7 @@ class ShadowCopyAction implements CopyAction { transformers.find { it.canTransformResource(element) }.transform( TransformerContext.builder() .path(mappedPath) - .is(is) + .inputStream(is) .relocators(relocators) .stats(stats) .build() diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.groovy deleted file mode 100644 index 569066ae1..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.groovy +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement - -/** - * Prevents duplicate copies of the license - *

- * Modified from org.apache.maven.plugins.shade.resouce.ApacheLicenseResourceTransformer.java - * - * @author John Engelman - */ -class ApacheLicenseResourceTransformer implements Transformer { - - private static final String LICENSE_PATH = "META-INF/LICENSE" - - private static final String LICENSE_TXT_PATH = "META-INF/LICENSE.txt" - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - return LICENSE_PATH.equalsIgnoreCase(path) || - LICENSE_TXT_PATH.regionMatches(true, 0, path, 0, LICENSE_TXT_PATH.length()) - } - - @Override - void transform(TransformerContext context) { - - } - - @Override - boolean hasTransformedResource() { - return false - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy index 77ef87fbc..3f894ce7a 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy @@ -113,9 +113,9 @@ class ApacheNoticeResourceTransformer implements Transformer { BufferedReader reader if (StringUtils.isNotEmpty(encoding)) { - reader = new BufferedReader(new InputStreamReader(context.is, encoding)) + reader = new BufferedReader(new InputStreamReader(context.inputStream, encoding)) } else { - reader = new BufferedReader(new InputStreamReader(context.is)) + reader = new BufferedReader(new InputStreamReader(context.inputStream)) } String line = reader.readLine() diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy index 7deb8ea50..be211b1a5 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy @@ -62,10 +62,10 @@ class AppendingTransformer implements Transformer { data = new ByteArrayOutputStream() } - IOUtil.copy(context.is, data) + IOUtil.copy(context.inputStream, data) data.write('\n'.bytes) - context.is.close() + context.inputStream.close() } @Override diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy index 58483d3cf..8321bc1c7 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy @@ -55,7 +55,7 @@ class ComponentsXmlResourceTransformer implements Transformer { Xpp3Dom newDom try { - BufferedInputStream bis = new BufferedInputStream(context.is) { + BufferedInputStream bis = new BufferedInputStream(context.inputStream) { void close() throws IOException { // leave ZIP open @@ -67,7 +67,7 @@ class ComponentsXmlResourceTransformer implements Transformer { newDom = Xpp3DomBuilder.build(reader) } catch (Exception e) { - throw (IOException) new IOException("Error parsing components.xml in " + context.is).initCause(e) + throw (IOException) new IOException("Error parsing components.xml in " + context.inputStream).initCause(e) } // Only try to merge in components if there are some elements in the component-set diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.groovy deleted file mode 100644 index 12eb55e10..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.groovy +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.StringUtils -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -/** - * A resource processor that prevents the inclusion of an arbitrary - * resource into the shaded JAR. - *

- * Modified from org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java - * - * @author John Engelman - */ -class DontIncludeResourceTransformer implements Transformer { - - @Optional - @Input - String resource - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (StringUtils.isNotEmpty(resource) && path.endsWith(resource)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - // no op - } - - @Override - boolean hasTransformedResource() { - return false - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - // no op - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy index 62255f3df..0770baae2 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy @@ -71,7 +71,7 @@ class GroovyExtensionModuleTransformer implements Transformer { @Override void transform(TransformerContext context) { def props = new Properties() - props.load(context.is) + props.load(context.inputStream) props.each { String key, String value -> switch (key) { case MODULE_NAME_KEY: diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.groovy deleted file mode 100644 index 3a829e873..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.groovy +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.PathSensitive -import org.gradle.api.tasks.PathSensitivity - -/** - * A resource processor that allows the addition of an arbitrary file - * content into the shaded JAR. - *

- * Modified from org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java - * - * @author John Engelman - */ -class IncludeResourceTransformer implements Transformer { - - @InputFile - @PathSensitive(PathSensitivity.NONE) - File file - - @Input - String resource - - @Override - boolean canTransformResource(FileTreeElement element) { - return false - } - - @Override - void transform(TransformerContext context) { - // no op - } - - @Override - boolean hasTransformedResource() { - return file != null ? file.exists() : false - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - - InputStream is = new FileInputStream(file) - IOUtil.copy(is, os) - is.close() - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy index 7b2d831f8..f3b04a873 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy @@ -61,7 +61,7 @@ class Log4j2PluginsCacheFileTransformer implements Transformer { @Override void transform(TransformerContext context) { - def inputStream = context.is + def inputStream = context.inputStream def temporaryFile = File.createTempFile("Log4j2Plugins", ".dat") temporaryFile.deleteOnExit() temporaryFiles.add(temporaryFile) @@ -126,4 +126,4 @@ class Log4j2PluginsCacheFileTransformer implements Transformer { } } } -} \ No newline at end of file +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy index bff6601d5..656228c8f 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy @@ -61,9 +61,9 @@ class ManifestAppenderTransformer implements Transformer { @Override void transform(TransformerContext context) { if (manifestContents.length == 0) { - manifestContents = IOUtil.toByteArray(context.is) + manifestContents = IOUtil.toByteArray(context.inputStream) try { - context.is + context.inputStream } catch (IOException e) { log.warn("Failed to read MANIFEST.MF", e) } diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy index cbc225a55..ccba8b699 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy @@ -73,10 +73,10 @@ class ManifestResourceTransformer implements Transformer { // now which is situational at best. Right now there is no context passed in with the processing so we cannot // tell what artifact is being processed. if (!manifestDiscovered) { - manifest = new Manifest(context.is) + manifest = new Manifest(context.inputStream) manifestDiscovered = true try { - context.is + context.inputStream } catch (IOException e) { log.warn("Failed to read MANIFEST.MF", e) } diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy index 044e3dbaa..64c230db3 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy @@ -157,7 +157,7 @@ class PropertiesFileTransformer implements Transformer { @Override void transform(TransformerContext context) { Properties props = propertiesEntries[context.path] - Properties incoming = loadAndTransformKeys(context.is) + Properties incoming = loadAndTransformKeys(context.inputStream) if (props == null) { propertiesEntries[context.path] = incoming } else { diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy index b2153a3f9..78f3184d9 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy @@ -68,7 +68,7 @@ class ServiceFileTransformer implements Transformer, PatternFilterable { @Override void transform(TransformerContext context) { - def lines = context.is.readLines() + def lines = context.inputStream.readLines() def targetPath = context.path context.relocators.each {rel -> if (rel.canRelocateClass(new File(targetPath).name)) { diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.groovy deleted file mode 100644 index b951dfa66..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.groovy +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction -import groovy.transform.Canonical -import groovy.transform.builder.Builder - - -@Canonical -@Builder -class TransformerContext { - - String path - InputStream is - List relocators - ShadowStats stats - - static long getEntryTimestamp(boolean preserveFileTimestamps, long entryTime) { - preserveFileTimestamps ? entryTime : ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy index 76e12f06d..7d08c02d5 100644 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy +++ b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy @@ -81,7 +81,7 @@ class XmlAppendingTransformer implements Transformer { } }) } - r = builder.build(context.is) + r = builder.build(context.inputStream) } catch (JDOMException e) { throw new RuntimeException("Error processing resource " + resource + ": " + e.getMessage(), e) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy index 7f93dca88..813160d82 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy @@ -81,7 +81,7 @@ class ApacheNoticeResourceTransformerParameterTests extends TransformerTestSuppo try { final ByteArrayInputStream noticeInputStream = new ByteArrayInputStream(noticeText.getBytes()) final List emptyList = Collections.emptyList() - transformer.transform(TransformerContext.builder().path(NOTICE_RESOURCE).is(noticeInputStream).relocators(emptyList).stats(stats).build()) + transformer.transform(TransformerContext.builder().path(NOTICE_RESOURCE).inputStream(noticeInputStream).relocators(emptyList).stats(stats).build()) } catch (NullPointerException ignored) { fail("Null pointer should not be thrown when no parameters are set.") diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy index b62fd1bc8..a6e04d92d 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy @@ -54,14 +54,14 @@ class ComponentsXmlResourceTransformerTest extends TransformerTestSupport emptyList()) .stats(stats) .build()) transformer.transform( TransformerContext.builder() .path("components-1.xml") - .is(getClass().getResourceAsStream("/components-2.xml")) + .inputStream(getClass().getResourceAsStream("/components-2.xml")) .relocators(Collections. emptyList()) .stats(stats) .build()) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy index becdaf25e..d767ee75d 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy @@ -27,7 +27,7 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { void "should not transformer"() { when: - transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), null)) + transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE))) then: !transformer.hasTransformedResource() diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy index 2d186a00e..e8298b2ef 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy @@ -23,7 +23,7 @@ final class PropertiesFileTransformerTest extends TransformerTestSupport Date: Mon, 18 Nov 2024 08:47:26 -0500 Subject: [PATCH 006/941] Kotlin migration part 4 (#1021) * Convert AbstractDependencyFilter * Convert DefaultDependencyFilter * Convert MinimizeDependencyFilter * Convert UnusedTracker * Simplify RelocatorRemapper * Convert RelocatorRemapper --- .../plugins/shadow/impl/RelocatorRemapper.kt | 64 +++++++++ .../internal/AbstractDependencyFilter.kt | 105 ++++++++++++++ .../internal/DefaultDependencyFilter.kt | 20 +++ .../internal/MinimizeDependencyFilter.kt | 30 ++++ .../plugins/shadow/internal/UnusedTracker.kt | 91 +++++++++++++ .../shadow/impl/RelocatorRemapper.groovy | 120 ---------------- .../internal/AbstractDependencyFilter.groovy | 128 ------------------ .../internal/DefaultDependencyFilter.groovy | 25 ---- .../internal/MinimizeDependencyFilter.groovy | 29 ---- .../shadow/internal/UnusedTracker.groovy | 85 ------------ 10 files changed, 310 insertions(+), 387 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.groovy diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt new file mode 100644 index 000000000..be5353bf8 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt @@ -0,0 +1,64 @@ +package com.github.jengelman.gradle.plugins.shadow.impl + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction +import java.util.regex.Pattern +import org.objectweb.asm.commons.Remapper + +/** + * Modified from `org.apache.maven.plugins.shade.DefaultShader.java#RelocatorRemapper` + * + * @author John Engelman + */ +open class RelocatorRemapper( + private val relocators: List, + private val stats: ShadowStats, +) : Remapper() { + private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") + + open fun hasRelocators(): Boolean = relocators.isNotEmpty() + + override fun mapValue(value: Any): Any { + return if (value is String) { + map(value) + } else { + super.mapValue(value) + } + } + + override fun map(name: String): String { + var newName = name + var prefix = "" + var suffix = "" + + val matcher = classPattern.matcher(newName) + if (matcher.matches()) { + prefix = matcher.group(1) + "L" + suffix = "" + newName = matcher.group(2) + } + + for (relocator in relocators) { + if (relocator.canRelocateClass(newName)) { + val classContext = RelocateClassContext.builder().className(newName).stats(stats).build() + return prefix + relocator.relocateClass(classContext) + suffix + } else if (relocator.canRelocatePath(newName)) { + val pathContext = RelocatePathContext.builder().path(newName).stats(stats).build() + return prefix + relocator.relocatePath(pathContext) + suffix + } + } + + return name + } + + open fun mapPath(path: String): String { + return map(path.substring(0, path.indexOf('.'))) + } + + open fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String { + return mapPath(path.pathString) + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt new file mode 100644 index 000000000..087e27930 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -0,0 +1,105 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import groovy.lang.Closure +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ResolvedArtifact +import org.gradle.api.artifacts.ResolvedDependency +import org.gradle.api.file.FileCollection +import org.gradle.api.specs.Spec +import org.gradle.api.specs.Specs + +internal sealed class AbstractDependencyFilter( + private val project: Project, +) : DependencyFilter { + protected val includeSpecs: MutableList> = mutableListOf() + protected val excludeSpecs: MutableList> = mutableListOf() + + protected abstract fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) + + override fun resolve(configuration: FileCollection): FileCollection { + val includedDeps = mutableSetOf() + val excludedDeps = mutableSetOf() + configuration as Configuration + resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, includedDeps, excludedDeps) + return project.files(configuration.files) - + project.files(excludedDeps.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) + } + + override fun resolve(configurations: Collection): FileCollection { + return configurations.map { resolve(it) } + .reduceOrNull { acc, fileCollection -> acc + fileCollection } + ?: project.files() + } + + /** + * Exclude dependencies that match the provided spec. + */ + override fun exclude(spec: Spec): DependencyFilter = apply { + excludeSpecs.add(spec) + } + + /** + * Include dependencies that match the provided spec. + */ + override fun include(spec: Spec): DependencyFilter = apply { + includeSpecs.add(spec) + } + + /** + * Create a spec that matches the provided project notation on group, name, and version. + */ + override fun project(notation: Map): Spec { + return dependency(dependency = project.dependencies.project(notation)) + } + + /** + * Create a spec that matches the default configuration for the provided project path on group, name, and version. + */ + override fun project(notation: String): Spec { + return dependency( + dependency = project.dependencies.project( + mapOf( + "path" to notation, + "configuration" to "default", + ), + ), + ) + } + + /** + * Create a spec that matches dependencies using the provided notation on group, name, and version. + */ + override fun dependency(notation: Any): Spec { + return dependency(dependency = project.dependencies.create(notation)) + } + + /** + * Create a spec that matches the provided dependency on group, name, and version. + */ + override fun dependency(dependency: Dependency): Spec { + return Spec { resolvedDependency -> + (dependency.group == null || resolvedDependency.moduleGroup.matches(dependency.group!!.toRegex())) && + resolvedDependency.moduleName.matches(dependency.name.toRegex()) && + (dependency.version == null || resolvedDependency.moduleVersion.matches(dependency.version!!.toRegex())) + } + } + + /** + * Create a spec that matches the provided closure. + */ + override fun dependency(closure: Closure<*>): Spec { + return Specs.convertClosureToSpec(closure) + } + + protected fun ResolvedDependency.isIncluded(): Boolean { + val include = includeSpecs.isEmpty() || includeSpecs.any { it.isSatisfiedBy(this) } + val exclude = excludeSpecs.isNotEmpty() && excludeSpecs.any { it.isSatisfiedBy(this) } + return include && !exclude + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt new file mode 100644 index 000000000..25604eaf2 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt @@ -0,0 +1,20 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import org.gradle.api.Project +import org.gradle.api.artifacts.ResolvedDependency + +internal class DefaultDependencyFilter( + project: Project, +) : AbstractDependencyFilter(project) { + override fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) { + dependencies.forEach { + if (if (it.isIncluded()) includedDependencies.add(it) else excludedDependencies.add(it)) { + resolve(it.children, includedDependencies, excludedDependencies) + } + } + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt new file mode 100644 index 000000000..df6bf5bc2 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt @@ -0,0 +1,30 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import org.gradle.api.Project +import org.gradle.api.artifacts.ResolvedDependency + +internal class MinimizeDependencyFilter( + project: Project, +) : AbstractDependencyFilter(project) { + override fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) { + dependencies.forEach { + if (it.isIncluded() && !isParentExcluded(excludedDependencies, it)) { + includedDependencies.add(it) + } else { + excludedDependencies.add(it) + } + resolve(it.children, includedDependencies, excludedDependencies) + } + } + + private fun isParentExcluded( + excludedDependencies: Set, + dependency: ResolvedDependency, + ): Boolean { + return excludedDependencies.any { it in dependency.parents } + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt new file mode 100644 index 000000000..504459d60 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -0,0 +1,91 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import java.io.File +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.artifacts.SelfResolvingDependency +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.InputFiles +import org.vafer.jdependency.Clazzpath +import org.vafer.jdependency.ClazzpathUnit + +/** Tracks unused classes in the project classpath. */ +internal class UnusedTracker private constructor( + classDirs: Iterable, + classJars: FileCollection, + private val _toMinimize: FileCollection, +) { + private val projectUnits: List + private val cp = Clazzpath() + + init { + projectUnits = classDirs.map { cp.addClazzpathUnit(it) } + classJars.map { cp.addClazzpathUnit(it) } + } + + @get:InputFiles + val toMinimize: FileCollection get() = _toMinimize + + fun findUnused(): Set { + val unused = cp.clazzes.toMutableSet() + for (cpu in projectUnits) { + unused.removeAll(cpu.clazzes) + unused.removeAll(cpu.transitiveDependencies) + } + return unused.map { it.name }.toSet() + } + + fun addDependency(jarOrDir: File) { + if (_toMinimize.contains(jarOrDir)) { + cp.addClazzpathUnit(jarOrDir) + } + } + + companion object { + @JvmStatic + fun forProject( + apiJars: FileCollection, + sourceSetsClassesDirs: Iterable, + toMinimize: FileCollection, + ): UnusedTracker { + return UnusedTracker(sourceSetsClassesDirs, apiJars, toMinimize) + } + + @JvmStatic + fun getApiJarsFromProject(project: Project): FileCollection { + val apiDependencies = project.configurations.findByName("api")?.dependencies + ?: return project.files() + val runtimeConfiguration = project.configurations.findByName("runtimeClasspath") + ?: project.configurations.getByName("runtime") + val apiJars = mutableListOf() + apiDependencies.forEach { dep -> + when (dep) { + is ProjectDependency -> { + apiJars.addAll(getApiJarsFromProject(dep.dependencyProject)) + addJar(runtimeConfiguration, dep, apiJars) + } + is SelfResolvingDependency -> { + apiJars.addAll(dep.resolve()) + } + else -> { + addJar(runtimeConfiguration, dep, apiJars) + apiJars.add(runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } as File) + } + } + } + return project.files(apiJars) + } + + private fun addJar(config: Configuration, dep: Dependency, result: MutableList) { + config.find { isProjectDependencyFile(it, dep) }?.let { result.add(it) } + } + + private fun isProjectDependencyFile(file: File, dep: Dependency): Boolean { + val fileName = file.name + val dependencyName = dep.name + return fileName == "$dependencyName.jar" || + (fileName.startsWith("$dependencyName-") && fileName.endsWith(".jar")) + } + } +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.groovy deleted file mode 100644 index 5e23269e4..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.groovy +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.impl - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.RelativeArchivePath -import org.objectweb.asm.commons.Remapper - -import java.util.regex.Matcher -import java.util.regex.Pattern - -/** - * Modified from org.apache.maven.plugins.shade.DefaultShader.java#RelocatorRemapper - * - * @author John Engelman - */ -class RelocatorRemapper extends Remapper { - - private final Pattern classPattern = Pattern.compile("(\\[*)?L(.+)") - - List relocators - ShadowStats stats - - RelocatorRemapper(List relocators, ShadowStats stats) { - this.relocators = relocators - this.stats = stats - } - - boolean hasRelocators() { - return !relocators.empty - } - - @Override - Object mapValue(Object object) { - if (object instanceof String) { - String name = (String) object - String value = name - - String prefix = "" - String suffix = "" - - Matcher m = classPattern.matcher(name) - if (m.matches()) { - prefix = m.group(1) + "L" - suffix = "" - name = m.group(2) - } - - for (Relocator r : relocators) { - if (r.canRelocateClass(name)) { - RelocateClassContext classContext = RelocateClassContext.builder().className(name).stats(stats).build() - value = prefix + r.relocateClass(classContext) + suffix - break - } else if (r.canRelocatePath(name)) { - RelocatePathContext pathContext = RelocatePathContext.builder().path(name).stats(stats).build() - value = prefix + r.relocatePath(pathContext) + suffix - break - } - } - - return value - } - - return super.mapValue(object) - } - - @Override - String map(String name) { - String value = name - - String prefix = "" - String suffix = "" - - Matcher m = classPattern.matcher(name) - if (m.matches()) { - prefix = m.group(1) + "L" - suffix = "" - name = m.group(2) - } - - for (Relocator r : relocators) { - if (r.canRelocatePath(name)) { - RelocatePathContext pathContext = RelocatePathContext.builder().path(name).stats(stats).build() - value = prefix + r.relocatePath(pathContext) + suffix - break - } - } - - return value - } - - String mapPath(String path) { - map(path.substring(0, path.indexOf('.'))) - } - - String mapPath(RelativeArchivePath path) { - mapPath(path.pathString) - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy deleted file mode 100644 index 160d34c15..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.groovy +++ /dev/null @@ -1,128 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import groovy.util.logging.Slf4j -import org.gradle.api.Project -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ResolvedDependency -import org.gradle.api.file.FileCollection -import org.gradle.api.specs.Spec -import org.gradle.api.specs.Specs - -@Slf4j -abstract class AbstractDependencyFilter implements DependencyFilter { - private final Project project - - protected final List> includeSpecs = [] - protected final List> excludeSpecs = [] - - AbstractDependencyFilter(Project project) { - assert project - this.project = project - } - - abstract protected void resolve(Set dependencies, - Set includedDependencies, - Set excludedDependencies) - - @Override - FileCollection resolve(FileCollection configuration) { - Set includedDeps = [] - Set excludedDeps = [] - resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, includedDeps, excludedDeps) - return project.files(configuration.files) - project.files(excludedDeps.collect { - it.moduleArtifacts*.file - }.flatten()) - } - - @Override - FileCollection resolve(Collection configurations) { - configurations.collect { - resolve(it) - }.sum() as FileCollection ?: project.files() - } - - /** - * Exclude dependencies that match the provided spec. - * - * @param spec - * @return - */ - @Override - DependencyFilter exclude(Spec spec) { - excludeSpecs << spec - return this - } - - /** - * Include dependencies that match the provided spec. - * - * @param spec - * @return - */ - @Override - DependencyFilter include(Spec spec) { - includeSpecs << spec - return this - } - - /** - * Create a spec that matches the provided project notation on group, name, and version - * @param notation - * @return - */ - @Override - Spec project(Map notation) { - dependency(project.dependencies.project(notation)) - } - - /** - * Create a spec that matches the default configuration for the provided project path on group, name, and version - * - * @param notation - * @return - */ - @Override - Spec project(String notation) { - dependency(project.dependencies.project(path: notation, configuration: 'default')) - } - - /** - * Create a spec that matches dependencies using the provided notation on group, name, and version - * @param notation - * @return - */ - @Override - Spec dependency(Object notation) { - dependency(project.dependencies.create(notation)) - } - - /** - * Create a spec that matches the provided dependency on group, name, and version - * @param dependency - * @return - */ - @Override - Spec dependency(Dependency dependency) { - this.dependency({ ResolvedDependency it -> - (!dependency.group || it.moduleGroup.matches(dependency.group)) && - (!dependency.name || it.moduleName.matches(dependency.name)) && - (!dependency.version || it.moduleVersion.matches(dependency.version)) - }) - } - - /** - * Create a spec that matches the provided closure - * @param spec - * @return - */ - @Override - Spec dependency(Closure spec) { - return Specs.convertClosureToSpec(spec) - } - - protected boolean isIncluded(ResolvedDependency dependency) { - boolean include = includeSpecs.empty || includeSpecs.any { it.isSatisfiedBy(dependency) } - boolean exclude = !excludeSpecs.empty && excludeSpecs.any { it.isSatisfiedBy(dependency) } - return include && !exclude - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.groovy deleted file mode 100644 index 4734fedd1..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import groovy.util.logging.Slf4j -import org.gradle.api.Project -import org.gradle.api.artifacts.ResolvedDependency - -@Slf4j -class DefaultDependencyFilter extends AbstractDependencyFilter { - - DefaultDependencyFilter(Project project) { - super(project) - } - - @Override - protected void resolve(Set dependencies, - Set includedDependencies, - Set excludedDependencies) { - dependencies.each { - if (isIncluded(it) ? includedDependencies.add(it) : excludedDependencies.add(it)) { - resolve(it.children, includedDependencies, excludedDependencies) - } - } - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.groovy deleted file mode 100644 index 4853c3a77..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.groovy +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import groovy.util.logging.Slf4j -import org.gradle.api.Project -import org.gradle.api.artifacts.ResolvedDependency - -@Slf4j -class MinimizeDependencyFilter extends AbstractDependencyFilter { - - MinimizeDependencyFilter(Project project) { - super(project) - } - - @Override - protected void resolve(Set dependencies, - Set includedDependencies, - Set excludedDependencies) { - - dependencies.each { - if (isIncluded(it) && !isParentExcluded(excludedDependencies, it) ? includedDependencies.add(it) : excludedDependencies.add(it)) { - resolve(it.children, includedDependencies, excludedDependencies) - } - } - } - - private static boolean isParentExcluded(Set excludedDependencies, ResolvedDependency dependency) { - excludedDependencies.any { dependency.parents.contains(it) } - } -} \ No newline at end of file diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.groovy deleted file mode 100644 index fcfbe6ea1..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.groovy +++ /dev/null @@ -1,85 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.SelfResolvingDependency -import org.gradle.api.file.FileCollection -import org.gradle.api.tasks.InputFiles -import org.vafer.jdependency.Clazz -import org.vafer.jdependency.Clazzpath -import org.vafer.jdependency.ClazzpathUnit - -/** Tracks unused classes in the project classpath. */ -class UnusedTracker { - private final FileCollection toMinimize - private final List projectUnits - private final Clazzpath cp = new Clazzpath() - - private UnusedTracker(Iterable classDirs, FileCollection classJars, FileCollection toMinimize) { - this.toMinimize = toMinimize - projectUnits = classDirs.collect { cp.addClazzpathUnit(it) } - projectUnits.addAll(classJars.collect { cp.addClazzpathUnit(it) }) - } - - Set findUnused() { - Set unused = cp.clazzes - for (cpu in projectUnits) { - unused.removeAll(cpu.clazzes) - unused.removeAll(cpu.transitiveDependencies) - } - return unused.collect { it.name }.toSet() - } - - void addDependency(File jarOrDir) { - if (toMinimize.contains(jarOrDir)) { - cp.addClazzpathUnit(jarOrDir) - } - } - - static UnusedTracker forProject(FileCollection apiJars, Iterable sourceSetsClassesDirs, FileCollection toMinimize) { - return new UnusedTracker(sourceSetsClassesDirs, apiJars, toMinimize) - } - - @InputFiles - FileCollection getToMinimize() { - return toMinimize - } - - private static boolean isProjectDependencyFile(File file, Dependency dep) { - def fileName = file.name - def dependencyName = dep.name - - return (fileName == "${dependencyName}.jar") || - (fileName.startsWith("${dependencyName}-") && fileName.endsWith('.jar')) - } - - private static void addJar(Configuration config, Dependency dep, List result) { - def file = config.find { isProjectDependencyFile(it, dep) } as File - if (file != null) { - result.add(file) - } - } - - static FileCollection getApiJarsFromProject(Project project) { - def apiDependencies = project.configurations.asMap['api']?.dependencies ?: null - if (apiDependencies == null) return project.files() - - def runtimeConfiguration = project.configurations.asMap['runtimeClasspath'] ?: project.configurations.runtime - def apiJars = new LinkedList() - apiDependencies.each { dep -> - if (dep instanceof ProjectDependency) { - apiJars.addAll(getApiJarsFromProject(dep.dependencyProject)) - addJar(runtimeConfiguration, dep, apiJars) - } else if (dep instanceof SelfResolvingDependency) { - apiJars.addAll(dep.resolve()) - } else { - addJar(runtimeConfiguration, dep, apiJars) - apiJars.add(runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } as File) - } - } - - return project.files(apiJars) - } -} From d756692976824ce86d212d07612a681a760893d6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 19 Nov 2024 03:52:44 -0500 Subject: [PATCH 007/941] Kotlin migration part 5 (#1023) * Convert AppendingTransformer * Inline AppendingTransformer._data * Convert XmlAppendingTransformer * Convert ManifestAppenderTransformer * Convert ManifestResourceTransformer --- .../transformers/AppendingTransformer.kt | 54 ++++++++ .../ManifestAppenderTransformer.kt | 74 +++++++++++ .../ManifestResourceTransformer.kt | 88 +++++++++++++ .../transformers/XmlAppendingTransformer.kt | 82 ++++++++++++ .../transformers/AppendingTransformer.groovy | 89 ------------- .../ManifestAppenderTransformer.groovy | 96 -------------- .../ManifestResourceTransformer.groovy | 123 ----------------- .../XmlAppendingTransformer.groovy | 124 ------------------ 8 files changed, 298 insertions(+), 432 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt new file mode 100644 index 000000000..f28b8014c --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -0,0 +1,54 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.ByteArrayOutputStream +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * A resource processor that appends content for a resource, separated by a newline. + * + * Modified from `org.apache.maven.plugins.shade.resource.AppendingTransformer.java` + * + * Modifications + * @author John Engelman + */ +@CacheableTransformer +@Suppress("ktlint:standard:backing-property-naming") +open class AppendingTransformer : Transformer { + /** + * Defer initialization, see [issue 763](https://github.com/GradleUp/shadow/issues/763). + */ + private var _data: ByteArrayOutputStream? = null + private inline val data get() = if (_data == null) ByteArrayOutputStream().also { _data = it } else _data!! + + @get:Optional + @get:Input + var resource: String? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + return resource.equals(element.relativePath.pathString, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + context.inputStream.use { + it.copyTo(data) + data.write('\n'.code) + } + } + + override fun hasTransformedResource(): Boolean { + return data.size() > 0 + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(requireNotNull(resource)) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + + data.toByteArray().inputStream().copyTo(os) + data.reset() + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt new file mode 100644 index 000000000..e925cc08b --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -0,0 +1,74 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.ByteArrayOutputStream +import java.io.IOException +import java.util.jar.JarFile.MANIFEST_NAME +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.slf4j.LoggerFactory + +/** + * A resource processor that can append arbitrary attributes to the first MANIFEST.MF + * that is found in the set of JARs being processed. The attributes are appended in + * the specified order, and duplicates are allowed. + * + * Modified from [ManifestResourceTransformer]. + * @author Chris Rankin + */ +open class ManifestAppenderTransformer : Transformer { + private var manifestContents = ByteArray(0) + private val _attributes = mutableListOf>>() + + @get:Input + open val attributes: List>> get() = _attributes + + override fun canTransformResource(element: FileTreeElement): Boolean { + return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + if (manifestContents.isEmpty()) { + try { + context.inputStream.use { inputStream -> + val outputStream = ByteArrayOutputStream() + inputStream.copyTo(outputStream) + manifestContents = outputStream.toByteArray() + } + } catch (e: IOException) { + logger.warn("Failed to read MANIFEST.MF", e) + } + } + } + + override fun hasTransformedResource(): Boolean = _attributes.isNotEmpty() + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(MANIFEST_NAME) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + os.write(manifestContents) + + if (_attributes.isNotEmpty()) { + for ((key, value) in _attributes) { + os.write(key.toByteArray()) + os.write(SEPARATOR) + os.write(value.toString().toByteArray()) + os.write(EOL) + } + os.write(EOL) + _attributes.clear() + } + } + + open fun append(name: String, value: Comparable<*>): ManifestAppenderTransformer = apply { + _attributes.add(Pair(name, value)) + } + + private companion object { + private val logger = LoggerFactory.getLogger(ManifestAppenderTransformer::class.java) + private val EOL = "\r\n".toByteArray() + private val SEPARATOR = ": ".toByteArray() + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt new file mode 100644 index 000000000..fb2ced9ce --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -0,0 +1,88 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp +import java.io.IOException +import java.util.jar.Attributes +import java.util.jar.JarFile +import java.util.jar.Manifest +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.slf4j.LoggerFactory + +/** + * A resource processor that allows the arbitrary addition of attributes to + * the first MANIFEST.MF that is found in the set of JARs being processed, or + * to a newly created manifest for the shaded JAR. + * + * Modified from `org.apache.maven.plugins.shade.resource.ManifestResourceTransformer` + * + * @author Jason van Zyl + * @author John Engelman + */ +open class ManifestResourceTransformer : Transformer { + private var manifestDiscovered = false + private var manifest: Manifest? = null + + @get:Optional + @get:Input + var mainClass: String? = null + + @get:Optional + @get:Input + var manifestEntries: MutableMap? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return JarFile.MANIFEST_NAME.equals(path, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + // We just want to take the first manifest we come across as that's our project's manifest. This is the behavior + // now which is situational at best. Right now there is no context passed in with the processing so we cannot + // tell what artifact is being processed. + if (!manifestDiscovered) { + try { + manifest = Manifest(context.inputStream) + manifestDiscovered = true + } catch (e: IOException) { + logger.warn("Failed to read MANIFEST.MF", e) + } + } + } + + override fun hasTransformedResource(): Boolean = true + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + // If we didn't find a manifest, then let's create one. + if (manifest == null) { + manifest = Manifest() + } + + val attributes = manifest!!.mainAttributes + mainClass?.let { + attributes[Attributes.Name.MAIN_CLASS] = it + } + manifestEntries?.forEach { (key, value) -> + attributes[Attributes.Name(key)] = value + } + + val entry = ZipEntry(JarFile.MANIFEST_NAME) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + manifest!!.write(os) + } + + open fun attributes(attributes: Map): ManifestResourceTransformer = apply { + if (manifestEntries == null) { + manifestEntries = LinkedHashMap() + } + manifestEntries!!.putAll(attributes) + } + + private companion object { + private val logger = LoggerFactory.getLogger(ManifestResourceTransformer::class.java) + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt new file mode 100644 index 000000000..1b5b6d0f8 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -0,0 +1,82 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.StringReader +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional +import org.jdom2.Document +import org.jdom2.JDOMException +import org.jdom2.input.SAXBuilder +import org.jdom2.input.sax.XMLReaders +import org.jdom2.output.Format +import org.jdom2.output.XMLOutputter +import org.xml.sax.EntityResolver +import org.xml.sax.InputSource + +/** + * Appends multiple occurrences of some XML file. + * + * Modified from `org.apache.maven.plugins.shade.resource.XmlAppendingTransformer.java` + * + * @author John Engelman + */ +@CacheableTransformer +open class XmlAppendingTransformer : Transformer { + private var doc: Document? = null + + @get:Input + var ignoreDtd: Boolean = true + + @get:Optional + @get:Input + var resource: String? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + return resource?.equals(element.relativePath.pathString, ignoreCase = true) == true + } + + override fun transform(context: TransformerContext) { + val r = try { + SAXBuilder(XMLReaders.NONVALIDATING).apply { + expandEntities = false + if (ignoreDtd) { + entityResolver = EntityResolver { _, _ -> InputSource(StringReader("")) } + } + }.build(context.inputStream) + } catch (e: JDOMException) { + throw RuntimeException("Error processing resource $resource: ${e.message}", e) + } + + if (doc == null) { + doc = r + } else { + val root = r.rootElement + root.attributes.forEach { a -> + val mergedEl = doc!!.rootElement + val mergedAtt = mergedEl.getAttribute(a.name, a.namespace) + if (mergedAtt == null) { + mergedEl.setAttribute(a) + } + } + root.children.forEach { n -> + doc!!.rootElement.addContent(n.clone()) + } + } + } + + override fun hasTransformedResource(): Boolean = doc != null + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(resource) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + XMLOutputter(Format.getPrettyFormat()).output(doc, os) + doc = null + } + + companion object { + const val XSI_NS: String = "http://www.w3.org/2001/XMLSchema-instance" + } +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy deleted file mode 100644 index be211b1a5..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.groovy +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -/** - * A resource processor that appends content for a resource, separated by a newline. - * - * Modified from org.apache.maven.plugins.shade.resource.AppendingTransformer.java - * - * Modifications - * @author John Engelman - */ -@CacheableTransformer -class AppendingTransformer implements Transformer { - - @Optional - @Input - String resource - - /** - * Defer initialization, see https://github.com/GradleUp/shadow/issues/763 - */ - private ByteArrayOutputStream data - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (resource != null && resource.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - if (data == null) { - data = new ByteArrayOutputStream() - } - - IOUtil.copy(context.inputStream, data) - data.write('\n'.bytes) - - context.inputStream.close() - } - - @Override - boolean hasTransformedResource() { - return data?.size() > 0 - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - if (data == null) { - data = new ByteArrayOutputStream() - } - - ZipEntry entry = new ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - - IOUtil.copy(new ByteArrayInputStream(data.toByteArray()), os) - data.reset() - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy deleted file mode 100644 index 656228c8f..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.groovy +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import groovy.util.logging.Slf4j -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input - -import static java.nio.charset.StandardCharsets.UTF_8 -import static java.util.jar.JarFile.MANIFEST_NAME - -/** - * A resource processor that can append arbitrary attributes to the first MANIFEST.MF - * that is found in the set of JARs being processed. The attributes are appended in - * the specified order, and duplicates are allowed. - *

- * Modified from {@link ManifestResourceTransformer}. - * @author Chris Rankin - */ -@Slf4j -class ManifestAppenderTransformer implements Transformer { - private static final byte[] EOL = "\r\n".getBytes(UTF_8) - private static final byte[] SEPARATOR = ": ".getBytes(UTF_8) - - private byte[] manifestContents = [] - private final List>> attributes = [] - - @Input - List>> getAttributes() { attributes } - - ManifestAppenderTransformer append(String name, Comparable value) { - attributes.add(new Tuple2>(name, value)) - this - } - - @Override - boolean canTransformResource(FileTreeElement element) { - MANIFEST_NAME.equalsIgnoreCase(element.relativePath.pathString) - } - - @Override - void transform(TransformerContext context) { - if (manifestContents.length == 0) { - manifestContents = IOUtil.toByteArray(context.inputStream) - try { - context.inputStream - } catch (IOException e) { - log.warn("Failed to read MANIFEST.MF", e) - } - } - } - - @Override - boolean hasTransformedResource() { - !attributes.isEmpty() - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(MANIFEST_NAME) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - os.write(manifestContents) - - if (!attributes.isEmpty()) { - for (attribute in attributes) { - os.write(attribute.v1.getBytes(UTF_8)) - os.write(SEPARATOR) - os.write(attribute.v2.toString().getBytes(UTF_8)) - os.write(EOL) - } - os.write(EOL) - attributes.clear() - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy deleted file mode 100644 index ccba8b699..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.groovy +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import groovy.util.logging.Slf4j -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -import java.util.jar.Attributes -import java.util.jar.Attributes.Name -import java.util.jar.JarFile -import java.util.jar.Manifest - -/** - * A resource processor that allows the arbitrary addition of attributes to - * the first MANIFEST.MF that is found in the set of JARs being processed, or - * to a newly created manifest for the shaded JAR. - *

- * Modified from org.apache.maven.plugins.shade.resource.ManifestResourceTransformer - * @author Jason van Zyl - * @author John Engelman - */ -@Slf4j -class ManifestResourceTransformer implements Transformer { - - // Configuration - @Optional - @Input - String mainClass - - @Optional - @Input - Map manifestEntries - - // Fields - private boolean manifestDiscovered - - private Manifest manifest - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (JarFile.MANIFEST_NAME.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - // We just want to take the first manifest we come across as that's our project's manifest. This is the behavior - // now which is situational at best. Right now there is no context passed in with the processing so we cannot - // tell what artifact is being processed. - if (!manifestDiscovered) { - manifest = new Manifest(context.inputStream) - manifestDiscovered = true - try { - context.inputStream - } catch (IOException e) { - log.warn("Failed to read MANIFEST.MF", e) - } - } - } - - @Override - boolean hasTransformedResource() { - return true - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - // If we didn't find a manifest, then let's create one. - if (manifest == null) { - manifest = new Manifest() - } - - Attributes attributes = manifest.getMainAttributes() - - if (mainClass != null) { - attributes.put(Name.MAIN_CLASS, mainClass) - } - - if (manifestEntries != null) { - for (Map.Entry entry : manifestEntries.entrySet()) { - attributes.put(new Name(entry.getKey()), entry.getValue()) - } - } - - ZipEntry entry = new ZipEntry(JarFile.MANIFEST_NAME) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - manifest.write(os) - } - - ManifestResourceTransformer attributes(Map attributes) { - if (manifestEntries == null) { - manifestEntries = [:] - } - manifestEntries.putAll(attributes) - this - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy deleted file mode 100644 index 7d08c02d5..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.groovy +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional -import org.jdom2.Attribute -import org.jdom2.Content -import org.jdom2.Document -import org.jdom2.Element -import org.jdom2.JDOMException -import org.jdom2.input.SAXBuilder -import org.jdom2.input.sax.XMLReaders -import org.jdom2.output.Format -import org.jdom2.output.XMLOutputter -import org.xml.sax.EntityResolver -import org.xml.sax.InputSource -import org.xml.sax.SAXException - -/** - * Appends multiple occurrences of some XML file. - *

- * Modified from org.apache.maven.plugins.shade.resource.XmlAppendingTransformer.java - * - * @author John Engelman - */ -@CacheableTransformer -class XmlAppendingTransformer implements Transformer { - public static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance" - - @Input - boolean ignoreDtd = true - - @Optional - @Input - String resource - - private Document doc - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (resource != null && resource.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - Document r - try { - SAXBuilder builder = new SAXBuilder(XMLReaders.NONVALIDATING) - builder.setExpandEntities(false) - if (ignoreDtd) { - builder.setEntityResolver(new EntityResolver() { - InputSource resolveEntity(String publicId, String systemId) - throws SAXException, IOException { - return new InputSource(new StringReader("")) - } - }) - } - r = builder.build(context.inputStream) - } - catch (JDOMException e) { - throw new RuntimeException("Error processing resource " + resource + ": " + e.getMessage(), e) - } - - if (doc == null) { - doc = r - } else { - Element root = r.getRootElement() - - root.attributes.each { Attribute a -> - - Element mergedEl = doc.getRootElement() - Attribute mergedAtt = mergedEl.getAttribute(a.getName(), a.getNamespace()) - if (mergedAtt == null) { - mergedEl.setAttribute(a) - } - } - - root.children.each { Content n -> - doc.getRootElement().addContent(n.clone()) - } - } - } - - @Override - boolean hasTransformedResource() { - return doc != null - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - new XMLOutputter(Format.getPrettyFormat()).output(doc, os) - - doc = null - } -} From aed9e11efecafce4f1010997b1116fbf49964d17 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Nov 2024 23:19:55 +0000 Subject: [PATCH 008/941] fix(deps): update dependency commons-io:commons-io to v2.18.0 (#1025) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- api/build.gradle.kts | 2 +- build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 8594b8a60..7aa77ec18 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -38,7 +38,7 @@ dependencies { implementation("org.jdom:jdom2:2.0.6.1") implementation("org.ow2.asm:asm-commons:9.7.1") - implementation("commons-io:commons-io:2.17.0") + implementation("commons-io:commons-io:2.18.0") implementation("org.apache.ant:ant:1.10.15") implementation("org.codehaus.plexus:plexus-utils:4.0.2") implementation("org.codehaus.plexus:plexus-xml:4.0.4") diff --git a/build.gradle.kts b/build.gradle.kts index 30d657f77..65de1e4aa 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -40,7 +40,7 @@ dependencies { implementation("org.jdom:jdom2:2.0.6.1") implementation("org.ow2.asm:asm-commons:9.7.1") - implementation("commons-io:commons-io:2.17.0") + implementation("commons-io:commons-io:2.18.0") implementation("org.apache.ant:ant:1.10.15") implementation("org.codehaus.plexus:plexus-utils:4.0.2") implementation("org.codehaus.plexus:plexus-xml:4.0.4") From 48a348283822b3241136d220195348bf9ed4da5e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 08:39:01 -0500 Subject: [PATCH 009/941] Kotlin migration part 6 (#1024) * Convert GroovyExtensionModuleTransformer * Convert Log4j2PluginsCacheFileTransformer * Simplify relocatePlugins * Convert SimpleRelocator * Simplify SimpleRelocator --- .../shadow/relocation/SimpleRelocator.kt | 156 +++++++++++ .../GroovyExtensionModuleTransformer.kt | 106 ++++++++ .../Log4j2PluginsCacheFileTransformer.kt | 90 +++++++ .../shadow/relocation/SimpleRelocator.groovy | 250 ------------------ .../GroovyExtensionModuleTransformer.groovy | 126 --------- .../Log4j2PluginsCacheFileTransformer.groovy | 129 --------- 6 files changed, 352 insertions(+), 505 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt new file mode 100644 index 000000000..4e3185eff --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -0,0 +1,156 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +import java.util.regex.Pattern +import org.codehaus.plexus.util.SelectorUtils +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * Modified from `org.apache.maven.plugins.shade.relocation.SimpleRelocator.java` + * + * @author Jason van Zyl + * @author Mauro Talevi + * @author John Engelman + */ +@CacheableRelocator +open class SimpleRelocator @JvmOverloads constructor( + pattern: String?, + shadedPattern: String?, + includes: List?, + excludes: List?, + private val _isRawString: Boolean = false, +) : Relocator { + private val _pattern: String? + private val _pathPattern: String + private val _shadedPattern: String? + private val _shadedPathPattern: String + private val _includes = mutableSetOf() + private val _excludes = mutableSetOf() + + init { + if (_isRawString) { + _pathPattern = pattern.orEmpty() + _shadedPathPattern = shadedPattern.orEmpty() + _pattern = null // not used for raw string relocator + _shadedPattern = null // not used for raw string relocator + } else { + if (pattern == null) { + _pattern = "" + _pathPattern = "" + } else { + _pattern = pattern.replace('/', '.') + _pathPattern = pattern.replace('.', '/') + } + if (shadedPattern == null) { + _shadedPattern = "hidden.${_pattern}" + _shadedPathPattern = "hidden/$_pathPattern" + } else { + _shadedPattern = shadedPattern.replace('/', '.') + _shadedPathPattern = shadedPattern.replace('.', '/') + } + } + _includes += normalizePatterns(includes) + _excludes += normalizePatterns(excludes) + } + + @get:Input + @get:Optional + open val pattern: String? get() = _pattern + + @get:Input + open val pathPattern: String get() = _pathPattern + + @get:Input + @get:Optional + open val shadedPattern: String? get() = _shadedPattern + + @get:Input + open val shadedPathPattern: String get() = _shadedPathPattern + + @get:Input + open val isRawString: Boolean get() = _isRawString + + @get:Input + open val includes: Set get() = _includes + + @get:Input + open val excludes: Set get() = _excludes + + open fun include(pattern: String): SimpleRelocator = apply { + _includes += normalizePatterns(listOf(pattern)) + } + + open fun exclude(pattern: String): SimpleRelocator = apply { + _excludes += normalizePatterns(listOf(pattern)) + } + + override fun canRelocatePath(path: String): Boolean { + if (_isRawString) return Pattern.compile(_pathPattern).matcher(path).find() + // If string is too short - no need to perform expensive string operations + if (path.length < _pathPattern.length) return false + val adjustedPath = if (path.endsWith(".class")) { + // Safeguard against strings containing only ".class" + if (path.length == 6) return false + path.dropLast(6) + } else { + path + } + // Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties"). + val startIndex = if (adjustedPath.startsWith("/")) 1 else 0 + val pathStartsWithPattern = adjustedPath.startsWith(_pathPattern, startIndex) + return pathStartsWithPattern && isIncluded(adjustedPath) && !isExcluded(adjustedPath) + } + + override fun canRelocateClass(className: String): Boolean { + return !_isRawString && !className.contains('/') && canRelocatePath(className.replace('.', '/')) + } + + override fun relocatePath(context: RelocatePathContext): String { + val path = context.path + context.stats.relocate(_pathPattern, _shadedPathPattern) + return if (_isRawString) { + path.replace(_pathPattern.toRegex(), _shadedPathPattern) + } else { + path.replaceFirst(_pathPattern, _shadedPathPattern) + } + } + + override fun relocateClass(context: RelocateClassContext): String { + context.stats.relocate(_pathPattern, _shadedPathPattern) + return context.className.replaceFirst(_pattern.orEmpty(), _shadedPattern.orEmpty()) + } + + override fun applyToSourceContent(sourceContent: String): String { + return if (_isRawString) { + sourceContent + } else { + sourceContent.replace("\\b$_pattern".toRegex(), _shadedPattern.orEmpty()) + } + } + + private fun normalizePatterns(patterns: Collection?) = buildSet { + for (pattern in patterns.orEmpty()) { + // Regex patterns don't need to be normalized and stay as is + if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { + add(pattern) + continue + } + + val classPattern = pattern.replace('.', '/') + add(classPattern) + + if (classPattern.endsWith("/*")) { + val packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) + add(packagePattern) + } + } + } + + private fun isIncluded(path: String): Boolean { + return _includes.isEmpty() || _includes.any { SelectorUtils.matchPath(it, path, "/", true) } + } + + private fun isExcluded(path: String): Boolean { + return _excludes.any { SelectorUtils.matchPath(it, path, "/", true) } + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt new file mode 100644 index 000000000..6b6e79687 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -0,0 +1,106 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.util.Properties +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement + +/** + * Modified from `eu.appsatori.gradle.fatjar.tasks.PrepareFiles.groovy` + * + * Resource transformer that merges Groovy extension module descriptor files into a single file. + * Groovy extension module descriptor files have the name org.codehaus.groovy.runtime.ExtensionModule + * and live in the META-INF/services (Groovy up to 2.4) or META-INF/groovy (Groovy 2.5+) directory. + * See [GROOVY-8480](https://issues.apache.org/jira/browse/GROOVY-8480) for more details of the change. + * + * If there are several descriptor files spread across many JARs the individual + * entries will be merged into a single descriptor file which will be + * packaged into the resultant JAR produced by the shadowing process. + * It will live in the legacy directory (META-INF/services) if all the processed descriptor + * files came from the legacy location, otherwise it will be written into the now standard location (META-INF/groovy). + * Note that certain JDK9+ tooling will break when using the legacy location. + */ +@CacheableTransformer +open class GroovyExtensionModuleTransformer : Transformer { + private val module = Properties() + + /** + * default to Groovy 2.4 or earlier + */ + private var legacy = true + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + if (path == GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH) { + // Groovy 2.5+ + legacy = false + return true + } + return path == GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH + } + + override fun transform(context: TransformerContext) { + val props = Properties() + props.load(context.inputStream) + props.forEach { key, value -> + when (key as String) { + MODULE_NAME_KEY -> handle(key, value as String) { + module.setProperty(key, MERGED_MODULE_NAME) + } + MODULE_VERSION_KEY -> handle(key, value as String) { + module.setProperty(key, MERGED_MODULE_VERSION) + } + EXTENSION_CLASSES_KEY, + STATIC_EXTENSION_CLASSES_KEY, + -> handle(key, value as String) { existingValue -> + module.setProperty(key, "$existingValue,$value") + } + } + } + } + + private fun handle(key: String, value: String, mergeValue: (String) -> Unit) { + val existingValue = module.getProperty(key) + if (existingValue != null) { + mergeValue(existingValue) + } else { + module.setProperty(key, value) + } + } + + override fun hasTransformedResource(): Boolean = module.isNotEmpty() + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val name = if (legacy) GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH else GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH + val entry = ZipEntry(name) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + module.inputStream().use { + it.copyTo(os) + } + os.closeEntry() + } + + private companion object { + private fun Properties.inputStream(): InputStream { + val os = ByteArrayOutputStream() + store(os, null) + return ByteArrayInputStream(os.toByteArray()) + } + + private const val GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH = + "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" + private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH = + "META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule" + private const val MODULE_NAME_KEY = "moduleName" + private const val MODULE_VERSION_KEY = "moduleVersion" + private const val EXTENSION_CLASSES_KEY = "extensionClasses" + private const val STATIC_EXTENSION_CLASSES_KEY = "staticExtensionClasses" + private const val MERGED_MODULE_NAME = "MergedByShadowJar" + private const val MERGED_MODULE_VERSION = "1.0.0" + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt new file mode 100644 index 000000000..37f565fe9 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -0,0 +1,90 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp +import java.io.File +import java.net.URL +import java.util.Collections +import java.util.Enumeration +import org.apache.commons.io.output.CloseShieldOutputStream +import org.apache.logging.log4j.core.config.plugins.processor.PluginCache +import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement + +/** + * Modified from the maven equivalent to work with gradle + * + * @author Paul Nelson Baker + * @see [LinkedIn](https://www.linkedin.com/in/paul-n-baker/) + * @see [GitHub](https://github.com/paul-nelson-baker/) + * @see [PluginsCacheFileTransformer.java](https://github.com/edwgiz/maven-shaded-log4j-transformer/blob/master/src/main/java/com/github/edwgiz/mavenShadePlugin/log4j2CacheTransformer/PluginsCacheFileTransformer.java) + */ +@CacheableTransformer +open class Log4j2PluginsCacheFileTransformer : Transformer { + private val temporaryFiles = mutableListOf() + private val relocators = mutableListOf() + private var stats: ShadowStats? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + return PluginProcessor.PLUGIN_CACHE_FILE == element.name + } + + override fun transform(context: TransformerContext) { + val temporaryFile = File.createTempFile("Log4j2Plugins", ".dat") + temporaryFile.deleteOnExit() + temporaryFiles.add(temporaryFile) + val fos = temporaryFile.outputStream() + context.inputStream.use { + it.copyTo(fos) + } + + relocators.addAll(context.relocators) + + if (stats == null) { + stats = context.stats + } + } + + override fun hasTransformedResource(): Boolean { + // This functionality matches the original plugin, however, I'm not clear what + // the exact logic is. From what I can tell temporaryFiles should be never be empty + // if anything has been performed. + val hasTransformedMultipleFiles = temporaryFiles.size > 1 + val hasAtLeastOneFileAndRelocator = temporaryFiles.isNotEmpty() && relocators.isNotEmpty() + return hasTransformedMultipleFiles || hasAtLeastOneFileAndRelocator + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val pluginCache = PluginCache() + pluginCache.loadCacheFiles(urlEnumeration) + relocatePlugins(pluginCache) + val entry = ZipEntry(PluginProcessor.PLUGIN_CACHE_FILE) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + pluginCache.writeCache(CloseShieldOutputStream.wrap(os)) + temporaryFiles.clear() + } + + private fun relocatePlugins(pluginCache: PluginCache) { + pluginCache.allCategories.values.forEach { currentMap -> + currentMap.values.forEach { currentPluginEntry -> + val className = currentPluginEntry.className + val relocateClassContext = RelocateClassContext(className, requireNotNull(stats)) + relocators.firstOrNull { it.canRelocateClass(className) }?.let { relocator -> + // Then we perform that relocation and update the plugin entry to reflect the new value. + currentPluginEntry.className = relocator.relocateClass(relocateClassContext) + } + } + } + } + + private val urlEnumeration: Enumeration + get() { + val urls = temporaryFiles.map { it.toURI().toURL() } + return Collections.enumeration(urls) + } +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.groovy deleted file mode 100644 index 4f4ec7688..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.groovy +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.relocation - -import org.codehaus.plexus.util.SelectorUtils -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -import java.util.regex.Pattern - -/** - * Modified from org.apache.maven.plugins.shade.relocation.SimpleRelocator.java - * - * @author Jason van Zyl - * @author Mauro Talevi - * @author John Engelman - */ -@CacheableRelocator -class SimpleRelocator implements Relocator { - - private final String pattern - - private final String pathPattern - - private final String shadedPattern - - private final String shadedPathPattern - - private final Set includes - - private final Set excludes - - private final boolean rawString - - SimpleRelocator() { - - } - - SimpleRelocator(String patt, String shadedPattern, List includes, List excludes) { - this(patt, shadedPattern, includes, excludes, false) - } - - SimpleRelocator(String patt, String shadedPattern, List includes, List excludes, - boolean rawString) { - this.rawString = rawString - - if (rawString) { - this.pathPattern = patt - this.shadedPathPattern = shadedPattern - - this.pattern = null // not used for raw string relocator - this.shadedPattern = null // not used for raw string relocator - } else { - if (patt == null) { - this.pattern = "" - this.pathPattern = "" - } else { - this.pattern = patt.replace('/', '.') - this.pathPattern = patt.replace('.', '/') - } - - if (shadedPattern != null) { - this.shadedPattern = shadedPattern.replace('/', '.') - this.shadedPathPattern = shadedPattern.replace('.', '/') - } else { - this.shadedPattern = "hidden." + this.pattern - this.shadedPathPattern = "hidden/" + this.pathPattern - } - } - - this.includes = normalizePatterns(includes) - this.excludes = normalizePatterns(excludes) - } - - SimpleRelocator include(String pattern) { - this.includes.addAll normalizePatterns([pattern]) - return this - } - - SimpleRelocator exclude(String pattern) { - this.excludes.addAll normalizePatterns([pattern]) - return this - } - - private static Set normalizePatterns(Collection patterns) { - Set normalized = null - - if (patterns != null && !patterns.isEmpty()) { - normalized = new LinkedHashSet() - - for (String pattern : patterns) { - // Regex patterns don't need to be normalized and stay as is - if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { - normalized.add(pattern) - continue - } - - String classPattern = pattern.replace('.', '/') - - normalized.add(classPattern) - - if (classPattern.endsWith("/*")) { - String packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) - normalized.add(packagePattern) - } - } - } - - return normalized ?: [] - } - - private boolean isIncluded(String path) { - if (includes != null && !includes.isEmpty()) { - for (String include : includes) { - if (SelectorUtils.matchPath(include, path, '/', true)) { - return true - } - } - return false - } - return true - } - - private boolean isExcluded(String path) { - if (excludes != null && !excludes.isEmpty()) { - for (String exclude : excludes) { - if (SelectorUtils.matchPath(exclude, path, '/', true)) { - return true - } - } - } - return false - } - - @Override - boolean canRelocatePath(String path) { - if (rawString) { - return Pattern.compile(pathPattern).matcher(path).find() - } - - // If string is too short - no need to perform expensive string operations - if (path.length() < pathPattern.length()) { - return false - } - - if (path.endsWith(".class")) { - // Safeguard against strings containing only ".class" - if (path.length() == 6) { - return false - } - path = path.substring(0, path.length() - 6) - } - - // Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties"). - boolean pathStartsWithPattern = - path.charAt(0) == '/' ? path.startsWith(pathPattern, 1) : path.startsWith(pathPattern) - if (pathStartsWithPattern) { - return isIncluded(path) && !isExcluded(path) - } - return false - } - - @Override - boolean canRelocateClass(String className) { - return !rawString && - className.indexOf('/') < 0 && - canRelocatePath(className.replace('.', '/')) - } - - @Override - String relocatePath(RelocatePathContext context) { - String path = context.path - context.stats.relocate(pathPattern, shadedPathPattern) - if (rawString) { - return path.replaceAll(pathPattern, shadedPathPattern) - } else { - return path.replaceFirst(pathPattern, shadedPathPattern) - } - } - - @Override - String relocateClass(RelocateClassContext context) { - String clazz = context.className - context.stats.relocate(pathPattern, shadedPathPattern) - return clazz.replaceFirst(pattern, shadedPattern) - } - - @Override - String applyToSourceContent(String sourceContent) { - if (rawString) { - return sourceContent - } else { - return sourceContent.replaceAll("\\b" + pattern, shadedPattern) - } - } - - @Input - @Optional - String getPattern() { - return pattern - } - - @Input - String getPathPattern() { - return pathPattern - } - - @Input - @Optional - String getShadedPattern() { - return shadedPattern - } - - @Input - String getShadedPathPattern() { - return shadedPathPattern - } - - @Input - Set getIncludes() { - return includes - } - - @Input - Set getExcludes() { - return excludes - } - - @Input - boolean getRawString() { - return rawString - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy deleted file mode 100644 index 0770baae2..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.groovy +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement - -/** - * Modified from eu.appsatori.gradle.fatjar.tasks.PrepareFiles.groovy - *

- * Resource transformer that merges Groovy extension module descriptor files into a single file. - * Groovy extension module descriptor files have the name org.codehaus.groovy.runtime.ExtensionModule - * and live in the META-INF/services (Groovy up to 2.4) or META-INF/groovy (Groovy 2.5+) directory. - * See https://issues.apache.org/jira/browse/GROOVY-8480 for more details of the change. - * - * If there are several descriptor files spread across many JARs the individual - * entries will be merged into a single descriptor file which will be - * packaged into the resultant JAR produced by the shadowing process. - * It will live in the legacy directory (META-INF/services) if all of the processed descriptor - * files came from the legacy location, otherwise it will be written into the now standard location (META-INF/groovy). - * Note that certain JDK9+ tooling will break when using the legacy location. - */ -@CacheableTransformer -class GroovyExtensionModuleTransformer implements Transformer { - - private static final GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH = - "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" - private static final GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH = - "META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule" - - private static final MODULE_NAME_KEY = 'moduleName' - private static final MODULE_VERSION_KEY = 'moduleVersion' - private static final EXTENSION_CLASSES_KEY = 'extensionClasses' - private static final STATIC_EXTENSION_CLASSES_KEY = 'staticExtensionClasses' - - private static final MERGED_MODULE_NAME = 'MergedByShadowJar' - private static final MERGED_MODULE_VERSION = '1.0.0' - - private final Properties module = new Properties() - private boolean legacy = true // default to Groovy 2.4 or earlier - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (path == GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH) { - legacy = false // Groovy 2.5+ - return true - } - return path == GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH - } - - @Override - void transform(TransformerContext context) { - def props = new Properties() - props.load(context.inputStream) - props.each { String key, String value -> - switch (key) { - case MODULE_NAME_KEY: - handle(key, value) { - module.setProperty(key, MERGED_MODULE_NAME) - } - break - case MODULE_VERSION_KEY: - handle(key, value) { - module.setProperty(key, MERGED_MODULE_VERSION) - } - break - case [EXTENSION_CLASSES_KEY, STATIC_EXTENSION_CLASSES_KEY]: - handle(key, value) { String existingValue -> - def newValue = "${existingValue},${value}" - module.setProperty(key, newValue) - } - break - } - } - } - - private handle(String key, String value, Closure mergeValue) { - def existingValue = module.getProperty(key) - if (existingValue) { - mergeValue(existingValue) - } else { - module.setProperty(key, value) - } - } - - @Override - boolean hasTransformedResource() { - return module.size() > 0 - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry entry = new ZipEntry(legacy ? GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH : GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - IOUtil.copy(toInputStream(module), os) - os.closeEntry() - } - - private static InputStream toInputStream(Properties props) { - def baos = new ByteArrayOutputStream() - props.store(baos, null) - return new ByteArrayInputStream(baos.toByteArray()) - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy deleted file mode 100644 index f3b04a873..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.groovy +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import org.apache.commons.io.IOUtils -import org.apache.commons.io.output.CloseShieldOutputStream -import org.apache.logging.log4j.core.config.plugins.processor.PluginCache -import org.apache.logging.log4j.core.config.plugins.processor.PluginEntry -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement - -import static org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE - -/** - * Modified from the maven equivalent to work with gradle - * - * @author Paul Nelson Baker - * @see LinkedIn - * @see GitHub - * @see edwgiz/maven-shaded-log4j-transformer - * @see PluginsCacheFileTransformer.java - */ -@CacheableTransformer -class Log4j2PluginsCacheFileTransformer implements Transformer { - - private final List temporaryFiles - private final List relocators - - private ShadowStats stats - - Log4j2PluginsCacheFileTransformer() { - temporaryFiles = new ArrayList<>() - relocators = new ArrayList<>() - } - - @Override - boolean canTransformResource(FileTreeElement element) { - return PLUGIN_CACHE_FILE == element.name - } - - @Override - void transform(TransformerContext context) { - def inputStream = context.inputStream - def temporaryFile = File.createTempFile("Log4j2Plugins", ".dat") - temporaryFile.deleteOnExit() - temporaryFiles.add(temporaryFile) - FileOutputStream fos = new FileOutputStream(temporaryFile) - try { - IOUtils.copy(inputStream, fos) - } finally { - fos.close() - } - def contextRelocators = context.relocators - if (contextRelocators != null) { - this.relocators.addAll(contextRelocators) - } - if (this.stats == null) { - this.stats = context.stats - } - } - - @Override - boolean hasTransformedResource() { - // This functionality matches the original plugin, however, I'm not clear what - // the exact logic is. From what I can tell temporaryFiles should be never be empty - // if anything has been performed. - def hasTransformedMultipleFiles = temporaryFiles.size() > 1 - def hasAtLeastOneFileAndRelocator = !temporaryFiles.isEmpty() && !relocators.isEmpty() - def hasTransformedResources = hasTransformedMultipleFiles || hasAtLeastOneFileAndRelocator - return hasTransformedResources - } - - @Override - void modifyOutputStream(ZipOutputStream zipOutputStream, boolean preserveFileTimestamps) { - PluginCache pluginCache = new PluginCache() - pluginCache.loadCacheFiles(getUrlEnumeration()) - relocatePlugins(pluginCache) - ZipEntry entry = new ZipEntry(PLUGIN_CACHE_FILE) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - zipOutputStream.putNextEntry(entry) - pluginCache.writeCache(CloseShieldOutputStream.wrap(zipOutputStream)) - temporaryFiles.clear() - } - - private Enumeration getUrlEnumeration() { - def urls = temporaryFiles.collect({ it.toURI().toURL() }).asList() - return Collections.enumeration(urls) - } - - private void relocatePlugins(PluginCache pluginCache) { - for (Map currentMap : pluginCache.getAllCategories().values()) { - pluginEntryLoop: - for (PluginEntry currentPluginEntry : currentMap.values()) { - String className = currentPluginEntry.getClassName() - RelocateClassContext relocateClassContext = new RelocateClassContext(className, stats) - for (Relocator currentRelocator : relocators) { - // If we have a relocator that can relocate our current entry... - if (currentRelocator.canRelocateClass(className)) { - // Then we perform that relocation and update the plugin entry to reflect the new value. - String relocatedClassName = currentRelocator.relocateClass(relocateClassContext) - currentPluginEntry.setClassName(relocatedClassName) - continue pluginEntryLoop - } - } - } - } - } -} From 67ea28b53c28b19c2af982c8664f425c310158f2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 09:15:42 -0500 Subject: [PATCH 010/941] Kotlin migration part 7 (#1026) * Convert ServiceFileTransformer * Convert ComponentsXmlResourceTransformer * Simplify ComponentsXmlResourceTransformer --- .../ComponentsXmlResourceTransformer.kt | 134 ++++++++++ .../transformers/ServiceFileTransformer.kt | 116 +++++++++ .../ComponentsXmlResourceTransformer.groovy | 190 --------------- .../ServiceFileTransformer.groovy | 229 ------------------ 4 files changed, 250 insertions(+), 419 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt new file mode 100644 index 000000000..258623830 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -0,0 +1,134 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp +import java.io.BufferedInputStream +import java.io.ByteArrayOutputStream +import java.io.IOException +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.codehaus.plexus.util.xml.XmlStreamReader +import org.codehaus.plexus.util.xml.XmlStreamWriter +import org.codehaus.plexus.util.xml.Xpp3Dom +import org.codehaus.plexus.util.xml.Xpp3DomBuilder +import org.codehaus.plexus.util.xml.Xpp3DomWriter +import org.gradle.api.file.FileTreeElement + +/** + * A resource processor that aggregates plexus `components.xml` files. + * + * Modified from `org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java` + * + * @author John Engelman + */ +open class ComponentsXmlResourceTransformer : Transformer { + private val components = mutableMapOf() + + override fun canTransformResource(element: FileTreeElement): Boolean { + return COMPONENTS_XML_PATH == element.relativePath.pathString + } + + override fun transform(context: TransformerContext) { + val newDom = try { + val bis = object : BufferedInputStream(context.inputStream) { + @Throws(IOException::class) + override fun close() { + // leave ZIP open + } + } + Xpp3DomBuilder.build(XmlStreamReader(bis)) + } catch (e: Exception) { + throw IOException("Error parsing components.xml in ${context.inputStream}", e) + } + + // Only try to merge in components if there are some elements in the component-set + if (newDom.getChild("components") == null) return + + val children = newDom.getChild("components").getChildren("component") + for (component in children) { + var role: String? = getValue(component, "role") + role = getRelocatedClass(role, context) + setValue(component, "role", role) + + val roleHint = getValue(component, "role-hint") + + var impl: String? = getValue(component, "implementation") + impl = getRelocatedClass(impl, context) + setValue(component, "implementation", impl) + + val key = "$role:$roleHint" + // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing + // configuration carry over + components[key]?.getChild("configuration")?.let { + component.addChild(it) + } + + val requirements = component.getChild("requirements") + if (requirements != null && requirements.childCount > 0) { + for (r in requirements.childCount - 1 downTo 0) { + val requirement = requirements.getChild(r) + var requiredRole: String? = getValue(requirement, "role") + requiredRole = getRelocatedClass(requiredRole, context) + setValue(requirement, "role", requiredRole) + } + } + components[key] = component + } + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(COMPONENTS_XML_PATH) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + + transformedResource.inputStream().use { + it.copyTo(os) + } + components.clear() + } + + override fun hasTransformedResource(): Boolean = components.isNotEmpty() + + @get:Throws(IOException::class) + private val transformedResource: ByteArray + get() { + val os = ByteArrayOutputStream(1024 * 4) + XmlStreamWriter(os).use { writer -> + val dom = Xpp3Dom("component-set") + val componentDom = Xpp3Dom("components") + dom.addChild(componentDom) + for (component in components.values) { + componentDom.addChild(component) + } + Xpp3DomWriter.write(writer, dom) + } + return os.toByteArray() + } + + companion object { + const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" + + private fun getRelocatedClass(className: String?, context: TransformerContext): String? { + val stats = context.stats + if (!className.isNullOrEmpty()) { + for (relocator in context.relocators) { + if (relocator.canRelocateClass(className)) { + val relocateClassContext = RelocateClassContext(className, stats) + return relocator.relocateClass(relocateClassContext) + } + } + } + return className + } + + private fun getValue(dom: Xpp3Dom, element: String): String { + return dom.getChild(element).value.orEmpty() + } + + private fun setValue(dom: Xpp3Dom, element: String, value: String?) { + val child = dom.getChild(element) + if (child == null || value.isNullOrEmpty()) return + child.value = value + } + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt new file mode 100644 index 000000000..824ac063a --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -0,0 +1,116 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.IOException +import java.io.InputStream +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.util.PatternFilterable +import org.gradle.api.tasks.util.PatternSet + +/** + * Modified from `org.apache.maven.plugins.shade.resource.ServiceResourceTransformer.java` + * + * Resources transformer that appends entries in `META-INF/services` resources into + * a single resource. For example, if there are several `META-INF/services/org.apache.maven.project.ProjectBuilder` + * resources spread across many JARs the individual entries will all be concatenated into a single + * `META-INF/services/org.apache.maven.project.ProjectBuilder` resource packaged into the resultant JAR produced + * by the shading process. + * + * @author jvanzyl + * @author Charlie Knudsen + * @author John Engelman + */ +@CacheableTransformer +open class ServiceFileTransformer( + private val patternSet: PatternSet = PatternSet() + .include(SERVICES_PATTERN) + .exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN), +) : Transformer, + PatternFilterable by patternSet { + private val serviceEntries = mutableMapOf() + + override fun canTransformResource(element: FileTreeElement): Boolean { + val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element + return patternSet.asSpec.isSatisfiedBy(target) + } + + override fun transform(context: TransformerContext) { + val lines = context.inputStream.bufferedReader().readLines().toMutableList() + var targetPath = context.path + context.relocators.forEach { rel -> + if (rel.canRelocateClass(File(targetPath).name)) { + val classContext = RelocateClassContext.builder().className(targetPath).stats(context.stats).build() + targetPath = rel.relocateClass(classContext) + } + lines.forEachIndexed { i, line -> + if (rel.canRelocateClass(line)) { + val lineContext = RelocateClassContext.builder().className(line).stats(context.stats).build() + lines[i] = rel.relocateClass(lineContext) + } + } + } + lines.forEach { line -> + serviceEntries[targetPath] = serviceEntries.getOrDefault(targetPath, ServiceStream()).apply { + append(ByteArrayInputStream(line.toByteArray())) + } + } + } + + override fun hasTransformedResource(): Boolean = serviceEntries.isNotEmpty() + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + serviceEntries.forEach { (path, stream) -> + val entry = ZipEntry(path) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + stream.toInputStream().use { + it.copyTo(os) + } + os.closeEntry() + } + } + + /** + * {@inheritDoc} + */ + @Input + override fun getIncludes(): Set = patternSet.includes + + /** + * {@inheritDoc} + */ + @Input + override fun getExcludes(): Set = patternSet.excludes + + open fun setPath(path: String): PatternFilterable = apply { + patternSet.setIncludes(listOf("$path/**")) + } + + open class ServiceStream : ByteArrayOutputStream(1024) { + @Throws(IOException::class) + open fun append(inputStream: InputStream) { + if (count > 0 && buf[count - 1] != '\n'.code.toByte() && buf[count - 1] != '\r'.code.toByte()) { + val newline = "\n".toByteArray() + write(newline, 0, newline.size) + } + inputStream.use { + it.copyTo(this) + } + } + + open fun toInputStream(): InputStream = ByteArrayInputStream(buf, 0, count) + } + + private companion object { + private const val SERVICES_PATTERN = "META-INF/services/**" + private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN = + "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" + } +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy deleted file mode 100644 index 8321bc1c7..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.groovy +++ /dev/null @@ -1,190 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.codehaus.plexus.util.xml.XmlStreamReader -import org.codehaus.plexus.util.xml.XmlStreamWriter -import org.codehaus.plexus.util.xml.Xpp3Dom -import org.codehaus.plexus.util.xml.Xpp3DomBuilder -import org.codehaus.plexus.util.xml.Xpp3DomWriter -import org.gradle.api.file.FileTreeElement - -/** - * A resource processor that aggregates plexus components.xml files. - *

- * Modified from org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java - * - * @author John Engelman - */ -class ComponentsXmlResourceTransformer implements Transformer { - private Map components = new LinkedHashMap() - - public static final String COMPONENTS_XML_PATH = "META-INF/plexus/components.xml" - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - return COMPONENTS_XML_PATH == path - } - - @Override - void transform(TransformerContext context) { - Xpp3Dom newDom - - try { - BufferedInputStream bis = new BufferedInputStream(context.inputStream) { - void close() - throws IOException { - // leave ZIP open - } - } - - Reader reader = new XmlStreamReader(bis) - - newDom = Xpp3DomBuilder.build(reader) - } - catch (Exception e) { - throw (IOException) new IOException("Error parsing components.xml in " + context.inputStream).initCause(e) - } - - // Only try to merge in components if there are some elements in the component-set - if (newDom.getChild("components") == null) { - return - } - - Xpp3Dom[] children = newDom.getChild("components").getChildren("component") - - for (int i = 0; i < children.length; i++) { - Xpp3Dom component = children[i] - - String role = getValue(component, "role") - role = getRelocatedClass(role, context) - setValue(component, "role", role) - - String roleHint = getValue(component, "role-hint") - - String impl = getValue(component, "implementation") - impl = getRelocatedClass(impl, context) - setValue(component, "implementation", impl) - - String key = role + ':' + roleHint - if (components.containsKey(key)) { - // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing - // configuration carry over - - Xpp3Dom dom = components.get(key) - if (dom.getChild("configuration") != null) { - component.addChild(dom.getChild("configuration")) - } - } - - Xpp3Dom requirements = component.getChild("requirements") - if (requirements != null && requirements.getChildCount() > 0) { - for (int r = requirements.getChildCount() - 1; r >= 0; r--) { - Xpp3Dom requirement = requirements.getChild(r) - - String requiredRole = getValue(requirement, "role") - requiredRole = getRelocatedClass(requiredRole, context) - setValue(requirement, "role", requiredRole) - } - } - - components.put(key, component) - } - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - byte[] data = getTransformedResource() - - ZipEntry entry = new ZipEntry(COMPONENTS_XML_PATH) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - - os.putNextEntry(entry) - - IOUtil.copy(data, os) - - components.clear() - } - - @Override - boolean hasTransformedResource() { - return !components.isEmpty() - } - - private byte[] getTransformedResource() - throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * 4) - - try (Writer writer = new XmlStreamWriter(baos)) { - Xpp3Dom dom = new Xpp3Dom("component-set") - - Xpp3Dom componentDom = new Xpp3Dom("components") - - dom.addChild(componentDom) - - for (Xpp3Dom component : components.values()) { - componentDom.addChild(component) - } - - Xpp3DomWriter.write(writer, dom) - } - - return baos.toByteArray() - } - - private static String getRelocatedClass(String className, TransformerContext context) { - List relocators = context.relocators - ShadowStats stats = context.stats - if (className != null && className.length() > 0 && relocators != null) { - for (Relocator relocator : relocators) { - if (relocator.canRelocateClass(className)) { - RelocateClassContext relocateClassContext = new RelocateClassContext(className, stats) - return relocator.relocateClass(relocateClassContext) - } - } - } - - return className - } - - private static String getValue(Xpp3Dom dom, String element) { - Xpp3Dom child = dom.getChild(element) - - return (child != null && child.getValue() != null) ? child.getValue() : "" - } - - private static void setValue(Xpp3Dom dom, String element, String value) { - Xpp3Dom child = dom.getChild(element) - - if (child == null || value == null || value.length() <= 0) { - return - } - - child.setValue(value) - } - -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy deleted file mode 100644 index 78f3184d9..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.groovy +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement -import org.gradle.api.specs.Spec -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.util.PatternFilterable -import org.gradle.api.tasks.util.PatternSet -import org.codehaus.plexus.util.IOUtil - -/** - * Modified from org.apache.maven.plugins.shade.resource.ServiceResourceTransformer.java - *

- * Resources transformer that appends entries in META-INF/services resources into - * a single resource. For example, if there are several META-INF/services/org.apache.maven.project.ProjectBuilder - * resources spread across many JARs the individual entries will all be concatenated into a single - * META-INF/services/org.apache.maven.project.ProjectBuilder resource packaged into the resultant JAR produced - * by the shading process. - * - * @author jvanzyl - * @author Charlie Knudsen - * @author John Engelman - */ -@CacheableTransformer -class ServiceFileTransformer implements Transformer, PatternFilterable { - - private static final String SERVICES_PATTERN = "META-INF/services/**" - - private static final String GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN = - "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" - - private Map serviceEntries = [:].withDefault { new ServiceStream() } - - private final PatternSet patternSet = - new PatternSet().include(SERVICES_PATTERN).exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN) - - void setPath(String path) { - patternSet.setIncludes(["${path}/**"]) - } - - @Override - boolean canTransformResource(FileTreeElement element) { - FileTreeElement target = element instanceof ShadowCopyAction.ArchiveFileTreeElement ? element.asFileTreeElement() : element - return patternSet.asSpec.isSatisfiedBy(target) - } - - @Override - void transform(TransformerContext context) { - def lines = context.inputStream.readLines() - def targetPath = context.path - context.relocators.each {rel -> - if (rel.canRelocateClass(new File(targetPath).name)) { - targetPath = rel.relocateClass(RelocateClassContext.builder().className(targetPath).stats(context.stats).build()) - } - lines.eachWithIndex { String line, int i -> - if (rel.canRelocateClass(line)) { - def lineContext = RelocateClassContext.builder().className(line).stats(context.stats).build() - lines[i] = rel.relocateClass(lineContext) - } - } - } - lines.each {line -> serviceEntries[targetPath].append(new ByteArrayInputStream(line.getBytes()))} - } - - @Override - boolean hasTransformedResource() { - return serviceEntries.size() > 0 - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - serviceEntries.each { String path, ServiceStream stream -> - ZipEntry entry = new ZipEntry(path) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - IOUtil.copy(stream.toInputStream(), os) - os.closeEntry() - } - } - - static class ServiceStream extends ByteArrayOutputStream { - - ServiceStream(){ - super( 1024 ) - } - - void append(InputStream is ) throws IOException { - if ( super.count > 0 && super.buf[super.count - 1] != '\n' && super.buf[super.count - 1] != '\r' ) { - byte[] newline = '\n'.bytes - write(newline, 0, newline.length) - } - IOUtil.copy(is, this) - } - - InputStream toInputStream() { - return new ByteArrayInputStream( super.buf, 0, super.count ) - } - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer include(String... includes) { - patternSet.include(includes) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer include(Iterable includes) { - patternSet.include(includes) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer include(Spec includeSpec) { - patternSet.include(includeSpec) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer include(Closure includeSpec) { - patternSet.include(includeSpec) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer exclude(String... excludes) { - patternSet.exclude(excludes) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer exclude(Iterable excludes) { - patternSet.exclude(excludes) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer exclude(Spec excludeSpec) { - patternSet.exclude(excludeSpec) - return this - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer exclude(Closure excludeSpec) { - patternSet.exclude(excludeSpec) - return this - } - - /** - * {@inheritDoc} - */ - @Override - @Input - Set getIncludes() { - return patternSet.includes - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer setIncludes(Iterable includes) { - patternSet.includes = includes - return this - } - - /** - * {@inheritDoc} - */ - @Override - @Input - Set getExcludes() { - return patternSet.excludes - } - - /** - * {@inheritDoc} - */ - @Override - ServiceFileTransformer setExcludes(Iterable excludes) { - patternSet.excludes = excludes - return this - } -} From 22254996c08a8170e8fd0d37df1d4184ff961c1b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 09:37:38 -0500 Subject: [PATCH 011/941] Kotlin migration part 8 (#1027) * Convert ApacheNoticeResourceTransformer * Convert PropertiesFileTransformer --- .../ApacheNoticeResourceTransformer.kt | 178 ++++++++++++ .../transformers/PropertiesFileTransformer.kt | 227 ++++++++++++++++ .../ApacheNoticeResourceTransformer.groovy | 225 --------------- .../PropertiesFileTransformer.groovy | 257 ------------------ 4 files changed, 405 insertions(+), 482 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt new file mode 100644 index 000000000..2fa195e77 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -0,0 +1,178 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.PrintWriter +import java.nio.charset.Charset +import java.text.SimpleDateFormat +import java.util.Date +import java.util.TreeSet +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional + +/** + * Merges `META-INF/NOTICE.TXT` files. + * + * Modified from `org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer.java` + * + * @author John Engelman + */ +open class ApacheNoticeResourceTransformer : Transformer { + private val entries = mutableSetOf() + private val organizationEntries = mutableMapOf>() + private val charset get() = if (encoding.isNullOrEmpty()) Charsets.UTF_8 else Charset.forName(encoding) + + /** + * MSHADE-101 :: NullPointerException when projectName is missing + */ + @get:Input + var projectName: String = "" + + @get:Input + var addHeader: Boolean = true + + @get:Input + var preamble1: String = """ + // ------------------------------------------------------------------ + // NOTICE file corresponding to the section 4d of The Apache License, + // Version 2.0, in this case for + """.trimIndent() + + @get:Input + var preamble2: String = "\n// ------------------------------------------------------------------\n" + + @get:Input + var preamble3: String = "This product includes software developed at\n" + + @get:Input + var organizationName: String = "The Apache Software Foundation" + + @get:Input + var organizationURL: String = "http://www.apache.org/" + + @get:Input + var inceptionYear: String = "2006" + + @get:Optional + @get:Input + var copyright: String? = null + + /** + * The file encoding of the `NOTICE` file. + */ + @get:Optional + @get:Input + var encoding: String? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return NOTICE_PATH.equals(path, ignoreCase = true) || NOTICE_TXT_PATH.equals(path, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + if (entries.isEmpty()) { + val year = SimpleDateFormat("yyyy").format(Date()).let { + if (inceptionYear != it) "$inceptionYear-$it" else it + } + // add headers + if (addHeader) { + entries.add("$preamble1$projectName$preamble2") + } else { + entries.add("") + } + // fake second entry, we'll look for a real one later + entries.add("$projectName\nCopyright $year $organizationName\n") + entries.add("$preamble3$organizationName ($organizationURL).\n") + } + + val reader = context.inputStream.bufferedReader(charset) + var line = reader.readLine() + val sb = StringBuffer() + var currentOrg: MutableSet? = null + var lineCount = 0 + while (line != null) { + val trimmedLine = line.trim() + if (!trimmedLine.startsWith("//")) { + if (trimmedLine.isNotEmpty()) { + if (trimmedLine.startsWith("- ")) { + // resource-bundle 1.3 mode + if (lineCount == 1 && sb.toString().contains("This product includes/uses software(s) developed by")) { + currentOrg = organizationEntries.getOrPut(sb.toString().trim()) { TreeSet() } + sb.setLength(0) + } else if (sb.isNotEmpty() && currentOrg != null) { + currentOrg.add(sb.toString()) + sb.setLength(0) + } + } + sb.append(line).append("\n") + lineCount++ + } else { + val entry = sb.toString() + if (entry.startsWith(projectName) && entry.contains("Copyright ")) { + copyright = entry + } + if (currentOrg == null) { + entries.add(entry) + } else { + currentOrg.add(entry) + } + sb.setLength(0) + lineCount = 0 + currentOrg = null + } + } + + line = reader.readLine() + } + if (sb.isNotEmpty()) { + if (currentOrg == null) { + entries.add(sb.toString()) + } else { + currentOrg.add(sb.toString()) + } + } + } + + override fun hasTransformedResource(): Boolean = true + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val zipEntry = ZipEntry(NOTICE_PATH) + zipEntry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, zipEntry.time) + os.putNextEntry(zipEntry) + + val writer = PrintWriter(os.writer(charset)) + + var count = 0 + for (line in entries) { + count++ + if (line == copyright && count != 2) continue + if (count == 2 && copyright != null) { + writer.print(copyright) + writer.print('\n') + } else { + writer.print(line) + writer.print('\n') + } + if (count == 3) { + // do org stuff + for ((key, value) in organizationEntries) { + writer.print(key) + writer.print('\n') + for (l in value) { + writer.print(l) + } + writer.print('\n') + } + } + } + + writer.flush() + entries.clear() + } + + private companion object { + private const val NOTICE_PATH = "META-INF/NOTICE" + private const val NOTICE_TXT_PATH = "META-INF/NOTICE.txt" + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt new file mode 100644 index 000000000..ba5a2632b --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -0,0 +1,227 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties +import java.io.ByteArrayOutputStream +import java.io.InputStream +import java.io.InputStreamReader +import java.nio.charset.Charset +import java.util.Properties +import java.util.function.Function +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal + +/** + * Resources transformer that merges Properties files. + * + * The default merge strategy discards duplicate values coming from additional + * resources. This behavior can be changed by setting a value for the `mergeStrategy` + * property, such as 'first' (default), 'latest' or 'append'. If the merge strategy is + * 'latest' then the last value of a matching property entry will be used. If the + * merge strategy is 'append' then the property values will be combined, using a + * merge separator (default value is ','). The merge separator can be changed by + * setting a value for the `mergeSeparator` property. + * + * Say there are two properties files A and B with the + * following entries: + * + * **A** + * - key1 = value1 + * - key2 = value2 + * + * **B** + * - key2 = balue2 + * - key3 = value3 + * + * With `mergeStrategy = first` you get + * + * **C** + * - key1 = value1 + * - key2 = value2 + * - key3 = value3 + * + * With `mergeStrategy = latest` you get + * + * **C** + * - key1 = value1 + * - key2 = balue2 + * - key3 = value3 + * + * With `mergeStrategy = append` and `mergeSeparator = ;` you get + * + * **C** + * - key1 = value1 + * - key2 = value2;balue2 + * - key3 = value3 + * + * There are three additional properties that can be set: `paths`, `mappings`, + * and `keyTransformer`. + * The first contains a list of strings or regexes that will be used to determine if + * a path should be transformed or not. The merge strategy and merge separator are + * taken from the global settings. + * + * The `mappings` property allows you to define merge strategy and separator per + * path. If either `paths` or `mappings` is defined then no other path + * entries will be merged. `mappings` has precedence over `paths` if both + * are defined. + * + * If you need to transform keys in properties files, e.g. because they contain class + * names about to be relocated, you can set the `keyTransformer` property to a + * closure that receives the original key and returns the key name to be used. + * + * Example: + * ```groovy + * import org.codehaus.griffon.gradle.shadow.transformers.* + * tasks.named('shadowJar', ShadowJar) { + * transform(PropertiesFileTransformer) { + * paths = [ + * 'META-INF/editors/java.beans.PropertyEditor' + * ] + * keyTransformer = { key -> + * key.replaceAll('^(orig\.package\..*)$', 'new.prefix.$1') + * } + * } + * } + * ``` + * + * @author Andres Almiray + * @author Marc Philipp + */ +open class PropertiesFileTransformer : Transformer { + private val propertiesEntries = mutableMapOf() + private val _charset get() = Charset.forName(charset) + + @get:Input + var paths: List = listOf() + + @get:Input + var mappings: Map> = mapOf() + + /** + * Optional values: first, latest, append. + */ + @get:Input + var mergeStrategy: String = "first" + + @get:Input + var mergeSeparator: String = "," + + @get:Input + var charset: String = "ISO_8859_1" + + /** + * Use [java.util.function.Function] here for compatibility with Groovy and Java. + */ + @get:Internal + var keyTransformer: Function = IDENTITY + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + if (path in mappings) return true + for (key in mappings.keys) { + if (key.toRegex().containsMatchIn(path)) return true + } + if (path in paths) return true + for (p in paths) { + if (p.toRegex().containsMatchIn(path)) return true + } + return mappings.isEmpty() && paths.isEmpty() && path.endsWith(PROPERTIES_SUFFIX) + } + + override fun transform(context: TransformerContext) { + val props = propertiesEntries[context.path] + val incoming = loadAndTransformKeys(context.inputStream) + if (props == null) { + propertiesEntries[context.path] = incoming + } else { + for ((key, value) in incoming) { + if (props.containsKey(key)) { + when (mergeStrategyFor(context.path).lowercase()) { + "latest" -> props[key] = value + "append" -> props[key] = props.getProperty(key as String) + mergeSeparatorFor(context.path) + value + "first" -> Unit + else -> Unit + } + } else { + props[key] = value + } + } + } + } + + private fun loadAndTransformKeys(inputStream: InputStream): CleanProperties { + val props = CleanProperties() + // InputStream closed by caller, so we don't do it here. + props.load(inputStream.reader(_charset)) + return transformKeys(props) + } + + private fun transformKeys(properties: Properties): CleanProperties { + if (keyTransformer === IDENTITY) { + return properties as CleanProperties + } + val result = CleanProperties() + properties.forEach { (key, value) -> + result[keyTransformer.apply(key as String)] = value + } + return result + } + + private fun mergeStrategyFor(path: String): String { + mappings[path]?.let { + return it["mergeStrategy"] ?: mergeStrategy + } + for (key in mappings.keys) { + if (key.toRegex().containsMatchIn(path)) { + return mappings[key]?.get("mergeStrategy") ?: mergeStrategy + } + } + return mergeStrategy + } + + private fun mergeSeparatorFor(path: String): String { + mappings[path]?.let { + return it["mergeSeparator"] ?: mergeSeparator + } + for (key in mappings.keys) { + if (key.toRegex().containsMatchIn(path)) { + return mappings[key]?.get("mergeSeparator") ?: mergeSeparator + } + } + return mergeSeparator + } + + override fun hasTransformedResource(): Boolean { + return propertiesEntries.isNotEmpty() + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + // cannot close the writer as the OutputStream needs to remain open + val zipWriter = os.writer(_charset) + propertiesEntries.forEach { (path, props) -> + val entry = ZipEntry(path) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + props.toReader().use { + it.copyTo(zipWriter) + } + zipWriter.flush() + os.closeEntry() + } + } + + private fun Properties.toReader(): InputStreamReader { + val os = ByteArrayOutputStream() + os.writer(Charset.forName(charset)).use { writer -> + store(writer, "") + } + return os.toByteArray().inputStream().reader(_charset) + } + + private companion object { + private const val PROPERTIES_SUFFIX = ".properties" + private val IDENTITY = Function { it } + } +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy deleted file mode 100644 index 3f894ce7a..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.groovy +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.StringUtils -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional - -import java.text.SimpleDateFormat - -/** - * Merges META-INF/NOTICE.TXT files. - *

- * Modified from org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer.java - * - * @author John Engelman - */ -class ApacheNoticeResourceTransformer implements Transformer { - - private Set entries = new LinkedHashSet() - - private Map> organizationEntries = new LinkedHashMap>() - - @Input - String projectName = "" // MSHADE-101 :: NullPointerException when projectName is missing - - @Input - boolean addHeader = true - - @Input - String preamble1 = "// ------------------------------------------------------------------\n" + - "// NOTICE file corresponding to the section 4d of The Apache License,\n" + - "// Version 2.0, in this case for " - - @Input - String preamble2 = "\n// ------------------------------------------------------------------\n" - - @Input - String preamble3 = "This product includes software developed at\n" - - @Input - String organizationName = "The Apache Software Foundation" - - @Input - String organizationURL = "http://www.apache.org/" - - @Input - String inceptionYear = "2006" - - @Optional - @Input - String copyright - - /** - * The file encoding of the NOTICE file. - */ - @Optional - @Input - String encoding - - private static final String NOTICE_PATH = "META-INF/NOTICE" - - private static final String NOTICE_TXT_PATH = "META-INF/NOTICE.txt" - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (NOTICE_PATH.equalsIgnoreCase(path) || NOTICE_TXT_PATH.equalsIgnoreCase(path)) { - return true - } - - return false - } - - @Override - void transform(TransformerContext context) { - if (entries.isEmpty()) { - String year = new SimpleDateFormat("yyyy").format(new Date()) - if (inceptionYear != year) { - year = inceptionYear + "-" + year - } - - //add headers - if (addHeader) { - entries.add(preamble1 + projectName + preamble2) - } else { - entries.add("") - } - //fake second entry, we'll look for a real one later - entries.add(projectName + "\nCopyright " + year + " " + organizationName + "\n") - entries.add(preamble3 + organizationName + " (" + organizationURL + ").\n") - } - - BufferedReader reader - if (StringUtils.isNotEmpty(encoding)) { - reader = new BufferedReader(new InputStreamReader(context.inputStream, encoding)) - } else { - reader = new BufferedReader(new InputStreamReader(context.inputStream)) - } - - String line = reader.readLine() - StringBuffer sb = new StringBuffer() - Set currentOrg = null - int lineCount = 0 - while (line != null) { - String trimedLine = line.trim() - - if (!trimedLine.startsWith("//")) { - if (trimedLine.length() > 0) { - if (trimedLine.startsWith("- ")) { - //resource-bundle 1.3 mode - if (lineCount == 1 - && sb.toString().indexOf("This product includes/uses software(s) developed by") != -1) { - currentOrg = organizationEntries.get(sb.toString().trim()) - if (currentOrg == null) { - currentOrg = new TreeSet() - organizationEntries.put(sb.toString().trim(), currentOrg) - } - sb = new StringBuffer() - } else if (sb.length() > 0 && currentOrg != null) { - currentOrg.add(sb.toString()) - sb = new StringBuffer() - } - - } - sb.append(line).append("\n") - lineCount++ - } else { - String ent = sb.toString() - if (ent.startsWith(projectName) && ent.indexOf("Copyright ") != -1) { - copyright = ent - } - if (currentOrg == null) { - entries.add(ent) - } else { - currentOrg.add(ent) - } - sb = new StringBuffer() - lineCount = 0 - currentOrg = null - } - } - - line = reader.readLine() - } - if (sb.length() > 0) { - if (currentOrg == null) { - entries.add(sb.toString()) - } else { - currentOrg.add(sb.toString()) - } - } - } - - @Override - boolean hasTransformedResource() { - return true - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - ZipEntry zipEntry = new ZipEntry(NOTICE_PATH) - zipEntry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, zipEntry.time) - os.putNextEntry(zipEntry) - - Writer pow - if (StringUtils.isNotEmpty(encoding)) { - pow = new OutputStreamWriter(os, encoding) - } else { - pow = new OutputStreamWriter(os) - } - PrintWriter writer = new PrintWriter(pow) - - int count = 0 - for (String line : entries) { - ++count - if (line == copyright && count != 2) { - continue - } - - if (count == 2 && copyright != null) { - writer.print(copyright) - writer.print('\n') - } else { - writer.print(line) - writer.print('\n') - } - if (count == 3) { - //do org stuff - for (Map.Entry> entry : organizationEntries.entrySet()) { - writer.print(entry.getKey()) - writer.print('\n') - for (String l : entry.getValue()) { - writer.print(l) - } - writer.print('\n') - } - } - } - - writer.flush() - - entries.clear() - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy deleted file mode 100644 index 64c230db3..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.groovy +++ /dev/null @@ -1,257 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipOutputStream -import org.codehaus.plexus.util.IOUtil -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal - -import static groovy.lang.Closure.IDENTITY - -/** - * Resources transformer that merges Properties files. - * - *

The default merge strategy discards duplicate values coming from additional - * resources. This behavior can be changed by setting a value for the mergeStrategy - * property, such as 'first' (default), 'latest' or 'append'. If the merge strategy is - * 'latest' then the last value of a matching property entry will be used. If the - * merge strategy is 'append' then the property values will be combined, using a - * merge separator (default value is ','). The merge separator can be changed by - * setting a value for the mergeSeparator property.

- * - * Say there are two properties files A and B with the - * following entries: - * - * A - *
    - *
  • key1 = value1
  • - *
  • key2 = value2
  • - *
- * - * B - *
    - *
  • key2 = balue2
  • - *
  • key3 = value3
  • - *
- * - * With mergeStrategy = first you get - * - * C - *
    - *
  • key1 = value1
  • - *
  • key2 = value2
  • - *
  • key3 = value3
  • - *
- * - * With mergeStrategy = latest you get - * - * C - *
    - *
  • key1 = value1
  • - *
  • key2 = balue2
  • - *
  • key3 = value3
  • - *
- * - * With mergeStrategy = append and mergeSparator = ; you get - * - * C - *
    - *
  • key1 = value1
  • - *
  • key2 = value2;balue2
  • - *
  • key3 = value3
  • - *
- * - *

There are three additional properties that can be set: paths, mappings, - * and keyTransformer. - * The first contains a list of strings or regexes that will be used to determine if - * a path should be transformed or not. The merge strategy and merge separator are - * taken from the global settings.

- * - *

The mappings property allows you to define merge strategy and separator per - * path

. If either paths or mappings is defined then no other path - * entries will be merged. mappings has precedence over paths if both - * are defined.

- * - *

If you need to transform keys in properties files, e.g. because they contain class - * names about to be relocated, you can set the keyTransformer property to a - * closure that receives the original key and returns the key name to be used.

- * - *

Example:

- *
- * import org.codehaus.griffon.gradle.shadow.transformers.*
- * tasks.named('shadowJar', ShadowJar) {
- *     transform(PropertiesFileTransformer) {
- *         paths = [
- *             'META-INF/editors/java.beans.PropertyEditor'
- *         ]
- *         keyTransformer = { key ->
- *             key.replaceAll('^(orig\.package\..*)$', 'new.prefix.$1')
- *         }
- *     }
- * }
- * 
- * - * @author Andres Almiray - * @author Marc Philipp - */ -class PropertiesFileTransformer implements Transformer { - private static final String PROPERTIES_SUFFIX = '.properties' - - private Map propertiesEntries = [:] - - @Input - List paths = [] - - @Input - Map> mappings = [:] - - @Input - String mergeStrategy = 'first' // latest, append - - @Input - String mergeSeparator = ',' - - @Input - String charset = 'ISO_8859_1' - - @Internal - Closure keyTransformer = IDENTITY - - @Override - boolean canTransformResource(FileTreeElement element) { - def path = element.relativePath.pathString - if (mappings.containsKey(path)) return true - for (key in mappings.keySet()) { - if (path =~ /$key/) return true - } - - if (path in paths) return true - for (p in paths) { - if (path =~ /$p/) return true - } - - !mappings && !paths && path.endsWith(PROPERTIES_SUFFIX) - } - - @Override - void transform(TransformerContext context) { - Properties props = propertiesEntries[context.path] - Properties incoming = loadAndTransformKeys(context.inputStream) - if (props == null) { - propertiesEntries[context.path] = incoming - } else { - incoming.each { key, value -> - if (props.containsKey(key)) { - switch (mergeStrategyFor(context.path).toLowerCase()) { - case 'latest': - props.put(key, value) - break - case 'append': - props.put(key, props.getProperty(key) + mergeSeparatorFor(context.path) + value) - break - case 'first': - default: - // continue - break - } - } else { - props.put(key, value) - } - } - } - } - - private Properties loadAndTransformKeys(InputStream is) { - Properties props = new CleanProperties() - // InputStream closed by caller, so we don't do it here. - if (is != null) { - props.load(new InputStreamReader(is, charset)) - } - return transformKeys(props) - } - - private Properties transformKeys(Properties properties) { - if (keyTransformer == IDENTITY) { - return properties - } - def result = new CleanProperties() - properties.each { key, value -> - result.put(keyTransformer.call(key), value) - } - return result - } - - private String mergeStrategyFor(String path) { - if (mappings.containsKey(path)) { - return mappings.get(path).mergeStrategy ?: mergeStrategy - } - for (key in mappings.keySet()) { - if (path =~ /$key/) { - return mappings.get(key).mergeStrategy ?: mergeStrategy - } - } - - return mergeStrategy - } - - private String mergeSeparatorFor(String path) { - if (mappings.containsKey(path)) { - return mappings.get(path).mergeSeparator ?: mergeSeparator - } - for (key in mappings.keySet()) { - if (path =~ /$key/) { - return mappings.get(key).mergeSeparator ?: mergeSeparator - } - } - - return mergeSeparator - } - - @Override - boolean hasTransformedResource() { - propertiesEntries.size() > 0 - } - - @Override - void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) { - // cannot close the writer as the OutputStream needs to remain open - def zipWriter = new OutputStreamWriter(os, charset) - propertiesEntries.each { String path, Properties props -> - ZipEntry entry = new ZipEntry(path) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - IOUtil.copy(readerFor(props, charset), zipWriter) - zipWriter.flush() - os.closeEntry() - } - } - - private static InputStreamReader readerFor(Properties props, String charset) { - ByteArrayOutputStream baos = new ByteArrayOutputStream() - baos.withWriter(charset) { w -> - props.store(w, '') - } - new InputStreamReader(new ByteArrayInputStream(baos.toByteArray()), charset) - } -} From 2752c0eb279942a9d7a927f850692830f50366e2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 09:52:48 -0500 Subject: [PATCH 012/941] Kotlin migration: ShadowCopyAction (#1028) --- .../plugins/shadow/tasks/ShadowCopyAction.kt | 447 ++++++++++++++ .../shadow/transformers/TransformerContext.kt | 8 +- .../shadow/tasks/ShadowCopyAction.groovy | 544 ------------------ 3 files changed, 448 insertions(+), 551 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt new file mode 100644 index 000000000..244d3f53a --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -0,0 +1,447 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.impl.RelocatorRemapper +import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext +import java.io.File +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.util.GregorianCalendar +import java.util.zip.ZipException +import org.apache.tools.zip.UnixStat +import org.apache.tools.zip.Zip64RequiredException +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipFile +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.GradleException +import org.gradle.api.file.FileCopyDetails +import org.gradle.api.file.FilePermissions +import org.gradle.api.file.FileTreeElement +import org.gradle.api.file.RelativePath +import org.gradle.api.internal.DocumentationRegistry +import org.gradle.api.internal.file.CopyActionProcessingStreamAction +import org.gradle.api.internal.file.DefaultFilePermissions +import org.gradle.api.internal.file.DefaultFileTreeElement +import org.gradle.api.internal.file.copy.CopyAction +import org.gradle.api.internal.file.copy.CopyActionProcessingStream +import org.gradle.api.internal.file.copy.FileCopyDetailsInternal +import org.gradle.api.tasks.WorkResult +import org.gradle.api.tasks.WorkResults +import org.gradle.api.tasks.bundling.Zip +import org.gradle.api.tasks.util.PatternSet +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.commons.ClassRemapper +import org.slf4j.LoggerFactory + +open class ShadowCopyAction internal constructor( + private val zipFile: File, + private val compressor: ZipCompressor, + private val documentationRegistry: DocumentationRegistry, + private val encoding: String?, + private val transformers: List, + private val relocators: List, + private val patternSet: PatternSet, + private val stats: ShadowStats, + private val preserveFileTimestamps: Boolean, + private val minimizeJar: Boolean, + private val unusedTracker: UnusedTracker?, +) : CopyAction { + + constructor( + zipFile: File, + compressor: ZipCompressor, + documentationRegistry: DocumentationRegistry, + encoding: String?, + transformers: List, + relocators: List, + patternSet: PatternSet, + stats: ShadowStats, + preserveFileTimestamps: Boolean, + minimizeJar: Boolean, + ) : this( + zipFile, + compressor, + documentationRegistry, + encoding, + transformers, + relocators, + patternSet, + stats, + preserveFileTimestamps, + minimizeJar, + null, + ) + + override fun execute(stream: CopyActionProcessingStream): WorkResult { + val unusedClasses = if (minimizeJar && unusedTracker != null) { + stream.process( + object : BaseStreamAction() { + override fun visitFile(fileDetails: FileCopyDetails) { + // All project sources are already present, we just need + // to deal with JAR dependencies. + if (isArchive(fileDetails)) { + unusedTracker.addDependency(fileDetails.file) + } + } + }, + ) + unusedTracker.findUnused() + } else { + emptySet() + } + + val zipOutStream = try { + compressor.createArchiveOutputStream(zipFile) as ZipOutputStream + } catch (e: Exception) { + throw GradleException("Could not create ZIP '$zipFile'", e) + } + + try { + zipOutStream.use { outputStream -> + stream.process( + StreamAction( + outputStream, + encoding, + transformers, + relocators, + patternSet, + unusedClasses, + stats, + ), + ) + processTransformers(outputStream) + } + } catch (e: IOException) { + throw Zip64RequiredException( + "${e.cause?.message}\n\nTo build this archive, please enable the zip64 extension.\n" + + "See: ${documentationRegistry.getDslRefForProperty(Zip::class.java, "zip64")}", + ) + } + return WorkResults.didWork(true) + } + + private fun processTransformers(stream: ZipOutputStream) { + transformers.forEach { transformer -> + if (transformer.hasTransformedResource()) { + transformer.modifyOutputStream(stream, preserveFileTimestamps) + } + } + } + + private fun getArchiveTimeFor(timestamp: Long): Long { + return if (preserveFileTimestamps) timestamp else CONSTANT_TIME_FOR_ZIP_ENTRIES + } + + private fun setArchiveTimes(zipEntry: ZipEntry): ZipEntry { + if (!preserveFileTimestamps) { + zipEntry.time = CONSTANT_TIME_FOR_ZIP_ENTRIES + } + return zipEntry + } + + abstract class BaseStreamAction : CopyActionProcessingStreamAction { + protected fun isArchive(fileDetails: FileCopyDetails): Boolean { + return fileDetails.relativePath.pathString.endsWith(".jar") + } + + protected fun isClass(fileDetails: FileCopyDetails): Boolean { + return fileDetails.path.endsWith(".class") + } + + override fun processFile(details: FileCopyDetailsInternal) { + if (details.isDirectory) visitDir(details) else visitFile(details) + } + + protected open fun visitDir(dirDetails: FileCopyDetails) {} + + protected abstract fun visitFile(fileDetails: FileCopyDetails) + } + + private inner class StreamAction( + private val zipOutStr: ZipOutputStream, + encoding: String?, + private val transformers: List, + private val relocators: List, + private val patternSet: PatternSet, + private val unused: Set, + private val stats: ShadowStats, + ) : BaseStreamAction() { + private val remapper = RelocatorRemapper(relocators, stats) + private val visitedFiles = mutableSetOf() + + init { + if (encoding != null) { + this.zipOutStr.setEncoding(encoding) + } + } + + override fun visitFile(fileDetails: FileCopyDetails) { + if (!isArchive(fileDetails)) { + try { + val isClass = isClass(fileDetails) + if (!remapper.hasRelocators() || !isClass) { + if (!isTransformable(fileDetails)) { + val mappedPath = remapper.map(fileDetails.relativePath.pathString) + val archiveEntry = ZipEntry(mappedPath) + archiveEntry.time = getArchiveTimeFor(fileDetails.lastModified) + archiveEntry.unixMode = UnixStat.FILE_FLAG or fileDetails.permissions.toUnixNumeric() + zipOutStr.putNextEntry(archiveEntry) + fileDetails.copyTo(zipOutStr) + zipOutStr.closeEntry() + } else { + transform(fileDetails) + } + } else if (isClass && !isUnused(fileDetails.path)) { + remapClass(fileDetails) + } + recordVisit(fileDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $fileDetails to ZIP '$zipFile'.", e) + } + } else { + processArchive(fileDetails) + } + } + + private fun recordVisit(path: RelativePath): Boolean { + return visitedFiles.add(path.pathString) + } + + private fun processArchive(fileDetails: FileCopyDetails) { + stats.startJar() + ZipFile(fileDetails.file).use { archive -> + archive.entries.asSequence() + .map { + ArchiveFileTreeElement(RelativeArchivePath(it)) + } + .filter { + patternSet.asSpec.isSatisfiedBy(it.asFileTreeElement()) + }.forEach { archiveElement -> + if (archiveElement.relativePath.isFile) { + visitArchiveFile(archiveElement, archive) + } + } + } + stats.finishJar() + } + + private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { + if (recordVisit(archiveDir)) { + zipOutStr.putNextEntry(archiveDir.entry) + zipOutStr.closeEntry() + } + } + + private fun visitArchiveFile(archiveFile: ArchiveFileTreeElement, archive: ZipFile) { + val archiveFilePath = archiveFile.relativePath + if (archiveFile.isClassFile || !isTransformable(archiveFile)) { + if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { + if (!remapper.hasRelocators() || !archiveFile.isClassFile) { + copyArchiveEntry(archiveFilePath, archive) + } else { + remapClass(archiveFilePath, archive) + } + } + } else { + transform(archiveFile, archive) + } + } + + private fun addParentDirectories(file: RelativeArchivePath?) { + file?.let { + addParentDirectories(it.parent) + if (!it.isFile) { + visitArchiveDirectory(it) + } + } + } + + private fun isUnused(classPath: String): Boolean { + val classPathWithoutExtension = classPath.substringBeforeLast(".") + val className = classPathWithoutExtension.replace('/', '.') + val result = unused.contains(className) + if (result) { + logger.debug("Dropping unused class: $className") + } + return result + } + + private fun remapClass(file: RelativeArchivePath, archive: ZipFile) { + if (file.isClassFile) { + val zipEntry = setArchiveTimes(ZipEntry(remapper.mapPath(file) + ".class")) + addParentDirectories(RelativeArchivePath(zipEntry)) + remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) + } + } + + private fun remapClass(fileCopyDetails: FileCopyDetails) { + if (fileCopyDetails.name.endsWith(".class")) { + fileCopyDetails.file.inputStream().use { + remapClass(it, fileCopyDetails.path, fileCopyDetails.lastModified) + } + } + } + + /** + * Applies remapping to the given class with the specified relocation path. The remapped class is then written + * to the zip file. [classInputStream] is closed automatically to prevent future file leaks. + * See #364 and #408. + */ + private fun remapClass(classInputStream: InputStream, path: String, lastModified: Long) { + // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. + // Copying the original constant pool should be avoided because it would keep references + // to the original class names. This is not a problem at runtime (because these entries in the + // constant pool are never used), but confuses some tools such as Felix' maven-bundle-plugin + // that use the constant pool to determine the dependencies of a class. + val cw = ClassWriter(0) + val cr = ClassReader(classInputStream) + val cv = ClassRemapper(cw, remapper) + + try { + cr.accept(cv, ClassReader.EXPAND_FRAMES) + } catch (t: Throwable) { + throw GradleException("Error in ASM processing class $path", t) + } + + val renamedClass = cw.toByteArray() + // Temporarily remove the multi-release prefix. + val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() + val newPath = path.replace(multiReleasePrefix, "") + val mappedName = multiReleasePrefix + remapper.mapPath(newPath) + try { + // Now we put it back on so the class file is written out with the right extension. + val archiveEntry = ZipEntry("$mappedName.class") + archiveEntry.time = getArchiveTimeFor(lastModified) + zipOutStr.putNextEntry(archiveEntry) + renamedClass.inputStream().use { + it.copyTo(zipOutStr) + } + zipOutStr.closeEntry() + } catch (ignored: ZipException) { + logger.warn("We have a duplicate $mappedName in source project") + } + } + + private fun copyArchiveEntry(archiveFile: RelativeArchivePath, archive: ZipFile) { + val mappedPath = remapper.map(archiveFile.entry.name) + val entry = ZipEntry(mappedPath) + entry.time = getArchiveTimeFor(archiveFile.entry.time) + val mappedFile = RelativeArchivePath(entry) + addParentDirectories(mappedFile) + zipOutStr.putNextEntry(mappedFile.entry) + archive.getInputStream(archiveFile.entry).use { + it.copyTo(zipOutStr) + } + zipOutStr.closeEntry() + } + + override fun visitDir(dirDetails: FileCopyDetails) { + try { + // Trailing slash in name indicates that entry is a directory + val path = dirDetails.relativePath.pathString + "/" + val archiveEntry = ZipEntry(path) + archiveEntry.time = getArchiveTimeFor(dirDetails.lastModified) + archiveEntry.unixMode = UnixStat.DIR_FLAG or dirDetails.permissions.toUnixNumeric() + zipOutStr.putNextEntry(archiveEntry) + zipOutStr.closeEntry() + recordVisit(dirDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $dirDetails to ZIP '$zipFile'.", e) + } + } + + private fun transform(element: ArchiveFileTreeElement, archive: ZipFile) { + transformAndClose(element, archive.getInputStream(element.relativePath.entry)) + } + + private fun transform(details: FileCopyDetails) { + transformAndClose(details, details.file.inputStream()) + } + + private fun transformAndClose(element: FileTreeElement, inputStream: InputStream) { + inputStream.use { steam -> + val mappedPath = remapper.map(element.relativePath.pathString) + transformers.find { + it.canTransformResource(element) + }?.transform( + TransformerContext.builder() + .path(mappedPath) + .inputStream(steam) + .relocators(relocators) + .stats(stats) + .build(), + ) + } + } + + private fun isTransformable(element: FileTreeElement): Boolean { + return transformers.any { it.canTransformResource(element) } + } + } + + open inner class RelativeArchivePath( + open val entry: ZipEntry, + ) : RelativePath( + !entry.isDirectory, + // `dir/` will be split into ["dir", ""], we have to trim empty segments here. + *entry.name.split('/').filter(CharSequence::isNotEmpty).toTypedArray(), + ) { + open val isClassFile: Boolean get() = lastName.endsWith(".class") + + override fun getParent(): RelativeArchivePath? { + return if (segments.size <= 1) { + null + } else { + // Parent is always a directory so add / to the end of the path + val parentPath = segments.dropLast(1).joinToString("/") + "/" + val entry = setArchiveTimes(ZipEntry(parentPath)) + RelativeArchivePath(entry) + } + } + } + + open class ArchiveFileTreeElement( + private val archivePath: RelativeArchivePath, + ) : FileTreeElement { + open val isClassFile: Boolean get() = archivePath.isClassFile + + override fun isDirectory(): Boolean = archivePath.entry.isDirectory + + override fun getLastModified(): Long = archivePath.entry.lastModifiedDate.time + + override fun getSize(): Long = archivePath.entry.size + + override fun getName(): String = archivePath.pathString + + override fun getPath(): String = archivePath.lastName + + override fun getRelativePath(): RelativeArchivePath = archivePath + + @Deprecated("Deprecated in Java") + override fun getMode(): Int = archivePath.entry.unixMode + + override fun getPermissions(): FilePermissions = DefaultFilePermissions(mode) + + open fun asFileTreeElement(): FileTreeElement { + return DefaultFileTreeElement(null, RelativePath(!isDirectory, *archivePath.segments), null, null) + } + + override fun getFile(): File = throw UnsupportedOperationException() + + override fun open(): InputStream = throw UnsupportedOperationException() + + override fun copyTo(outputStream: OutputStream): Unit = throw UnsupportedOperationException() + + override fun copyTo(file: File): Boolean = throw UnsupportedOperationException() + } + + companion object { + private val logger = LoggerFactory.getLogger(ShadowCopyAction::class.java) + val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis + } +} diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt index e9d1b6cab..f80f19392 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -4,7 +4,6 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import java.io.InputStream -import java.util.GregorianCalendar data class TransformerContext @JvmOverloads constructor( val path: String, @@ -31,17 +30,12 @@ data class TransformerContext @JvmOverloads constructor( } companion object { - /** - * TODO: replace this with [ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES] - */ - private val CONSTANT_TIME_FOR_ZIP_ENTRIES = GregorianCalendar(1980, 1, 1, 0, 0, 0).getTimeInMillis() - @JvmStatic fun builder(): Builder = Builder() @JvmStatic fun getEntryTimestamp(preserveFileTimestamps: Boolean, entryTime: Long): Long { - return if (preserveFileTimestamps) entryTime else CONSTANT_TIME_FOR_ZIP_ENTRIES + return if (preserveFileTimestamps) entryTime else ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES } } } diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy deleted file mode 100644 index 7935af4f3..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.groovy +++ /dev/null @@ -1,544 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.impl.RelocatorRemapper -import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext -import groovy.util.logging.Slf4j -import org.apache.commons.io.FilenameUtils -import org.apache.commons.io.IOUtils -import org.apache.tools.zip.UnixStat -import org.apache.tools.zip.Zip64RequiredException -import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipFile -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.Action -import org.gradle.api.GradleException -import org.gradle.api.UncheckedIOException -import org.gradle.api.file.FileCopyDetails -import org.gradle.api.file.FilePermissions -import org.gradle.api.file.FileTreeElement -import org.gradle.api.file.RelativePath -import org.gradle.api.internal.DocumentationRegistry -import org.gradle.api.internal.file.CopyActionProcessingStreamAction -import org.gradle.api.internal.file.DefaultFilePermissions -import org.gradle.api.internal.file.DefaultFileTreeElement -import org.gradle.api.internal.file.copy.CopyAction -import org.gradle.api.internal.file.copy.CopyActionProcessingStream -import org.gradle.api.internal.file.copy.FileCopyDetailsInternal -import org.gradle.api.specs.Spec -import org.gradle.api.tasks.WorkResult -import org.gradle.api.tasks.WorkResults -import org.gradle.api.tasks.bundling.Zip -import org.gradle.api.tasks.util.PatternSet -import org.gradle.internal.UncheckedException -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassVisitor -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.commons.ClassRemapper - -import java.util.zip.ZipException - -@Slf4j -class ShadowCopyAction implements CopyAction { - static final long CONSTANT_TIME_FOR_ZIP_ENTRIES = (new GregorianCalendar(1980, 1, 1, 0, 0, 0)).getTimeInMillis() - - private final File zipFile - private final ZipCompressor compressor - private final DocumentationRegistry documentationRegistry - private final List transformers - private final List relocators - private final PatternSet patternSet - private final ShadowStats stats - private final String encoding - private final boolean preserveFileTimestamps - private final boolean minimizeJar - private final UnusedTracker unusedTracker - - ShadowCopyAction(File zipFile, ZipCompressor compressor, DocumentationRegistry documentationRegistry, - String encoding, List transformers, List relocators, - PatternSet patternSet, ShadowStats stats, - boolean preserveFileTimestamps, boolean minimizeJar, UnusedTracker unusedTracker) { - - this.zipFile = zipFile - this.compressor = compressor - this.documentationRegistry = documentationRegistry - this.transformers = transformers - this.relocators = relocators - this.patternSet = patternSet - this.stats = stats - this.encoding = encoding - this.preserveFileTimestamps = preserveFileTimestamps - this.minimizeJar = minimizeJar - this.unusedTracker = unusedTracker - } - - @Override - WorkResult execute(CopyActionProcessingStream stream) { - Set unusedClasses - if (minimizeJar) { - stream.process(new BaseStreamAction() { - @Override - void visitFile(FileCopyDetails fileDetails) { - // All project sources are already present, we just need - // to deal with JAR dependencies. - if (isArchive(fileDetails)) { - unusedTracker.addDependency(fileDetails.file) - } - } - }) - unusedClasses = unusedTracker.findUnused() - } else { - unusedClasses = Collections.emptySet() - } - - ZipOutputStream zipOutStr - - try { - zipOutStr = compressor.createArchiveOutputStream(zipFile) - } catch (Exception e) { - throw new GradleException("Could not create ZIP '${zipFile.toString()}'", e) - } - - try { - withResource(zipOutStr, new Action() { - void execute(ZipOutputStream outputStream) { - try { - stream.process(new StreamAction(outputStream, encoding, transformers, relocators, patternSet, - unusedClasses, stats)) - processTransformers(outputStream) - } catch (Exception e) { - log.error('ex', e) - //TODO this should not be rethrown - throw e - } - } - }) - } catch (UncheckedIOException e) { - if (e.cause instanceof Zip64RequiredException) { - throw new Zip64RequiredException( - String.format("%s\n\nTo build this archive, please enable the zip64 extension.\nSee: %s", - e.cause.message, documentationRegistry.getDslRefForProperty(Zip, "zip64")) - ) - } - } - return WorkResults.didWork(true) - } - - private void processTransformers(ZipOutputStream stream) { - transformers.each { Transformer transformer -> - if (transformer.hasTransformedResource()) { - transformer.modifyOutputStream(stream, preserveFileTimestamps) - } - } - } - - private long getArchiveTimeFor(long timestamp) { - return preserveFileTimestamps ? timestamp : CONSTANT_TIME_FOR_ZIP_ENTRIES - } - - private ZipEntry setArchiveTimes(ZipEntry zipEntry) { - if (!preserveFileTimestamps) { - zipEntry.setTime(CONSTANT_TIME_FOR_ZIP_ENTRIES) - } - return zipEntry - } - - private static void withResource(T resource, Action action) { - try { - action.execute(resource) - } catch(Throwable t) { - try { - resource.close() - } catch (IOException e) { - log.warn("Could not close resource $resource", e) - } - throw UncheckedException.throwAsUncheckedException(t) - } - - try { - resource.close() - } catch (IOException e) { - throw new UncheckedIOException(e) - } - } - - abstract class BaseStreamAction implements CopyActionProcessingStreamAction { - protected boolean isArchive(FileCopyDetails fileDetails) { - return fileDetails.relativePath.pathString.endsWith('.jar') - } - - protected boolean isClass(FileCopyDetails fileDetails) { - return FilenameUtils.getExtension(fileDetails.path) == 'class' - } - - @Override - void processFile(FileCopyDetailsInternal details) { - if (details.directory) { - visitDir(details) - } else { - visitFile(details) - } - } - - protected void visitDir(FileCopyDetails dirDetails) {} - - protected abstract void visitFile(FileCopyDetails fileDetails) - } - - private class StreamAction extends BaseStreamAction { - - private final ZipOutputStream zipOutStr - private final List transformers - private final List relocators - private final RelocatorRemapper remapper - private final PatternSet patternSet - private final Set unused - private final ShadowStats stats - - private Set visitedFiles = new HashSet() - - StreamAction(ZipOutputStream zipOutStr, String encoding, List transformers, - List relocators, PatternSet patternSet, Set unused, - ShadowStats stats) { - this.zipOutStr = zipOutStr - this.transformers = transformers - this.relocators = relocators - this.remapper = new RelocatorRemapper(relocators, stats) - this.patternSet = patternSet - this.unused = unused - this.stats = stats - if(encoding != null) { - this.zipOutStr.setEncoding(encoding) - } - } - - private boolean recordVisit(RelativePath path) { - return visitedFiles.add(path.pathString) - } - - @Override - void visitFile(FileCopyDetails fileDetails) { - if (!isArchive(fileDetails)) { - try { - boolean isClass = isClass(fileDetails) - if (!remapper.hasRelocators() || !isClass) { - if (!isTransformable(fileDetails)) { - String mappedPath = remapper.map(fileDetails.relativePath.pathString) - ZipEntry archiveEntry = new ZipEntry(mappedPath) - archiveEntry.setTime(getArchiveTimeFor(fileDetails.lastModified)) - archiveEntry.unixMode = (UnixStat.FILE_FLAG | fileDetails.permissions.toUnixNumeric()) - zipOutStr.putNextEntry(archiveEntry) - fileDetails.copyTo(zipOutStr) - zipOutStr.closeEntry() - } else { - transform(fileDetails) - } - } else if (isClass && !isUnused(fileDetails.path)) { - remapClass(fileDetails) - } - recordVisit(fileDetails.relativePath) - } catch (Exception e) { - throw new GradleException(String.format("Could not add %s to ZIP '%s'.", fileDetails, zipFile), e) - } - } else { - processArchive(fileDetails) - } - } - - private void processArchive(FileCopyDetails fileDetails) { - stats.startJar() - ZipFile archive = new ZipFile(fileDetails.file) - try { - List archiveElements = archive.entries.collect { - new ArchiveFileTreeElement(new RelativeArchivePath(it)) - } - Spec patternSpec = patternSet.getAsSpec() - List filteredArchiveElements = archiveElements.findAll { ArchiveFileTreeElement archiveElement -> - patternSpec.isSatisfiedBy(archiveElement.asFileTreeElement()) - } - filteredArchiveElements.each { ArchiveFileTreeElement archiveElement -> - if (archiveElement.relativePath.file) { - visitArchiveFile(archiveElement, archive) - } - } - } finally { - archive.close() - } - stats.finishJar() - } - - private void visitArchiveDirectory(RelativeArchivePath archiveDir) { - if (recordVisit(archiveDir)) { - zipOutStr.putNextEntry(archiveDir.entry) - zipOutStr.closeEntry() - } - } - - private void visitArchiveFile(ArchiveFileTreeElement archiveFile, ZipFile archive) { - def archiveFilePath = archiveFile.relativePath - if (archiveFile.classFile || !isTransformable(archiveFile)) { - if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { - if (!remapper.hasRelocators() || !archiveFile.classFile) { - copyArchiveEntry(archiveFilePath, archive) - } else { - remapClass(archiveFilePath, archive) - } - } - } else { - transform(archiveFile, archive) - } - } - - private void addParentDirectories(RelativeArchivePath file) { - if (file) { - addParentDirectories(file.parent) - if (!file.file) { - visitArchiveDirectory(file) - } - } - } - - private boolean isUnused(String classPath) { - final String className = FilenameUtils.removeExtension(classPath) - .replace('/' as char, '.' as char) - final boolean result = unused.contains(className) - if (result) { - log.debug("Dropping unused class: $className") - } - return result - } - - private void remapClass(RelativeArchivePath file, ZipFile archive) { - if (file.classFile) { - ZipEntry zipEntry = setArchiveTimes(new ZipEntry(remapper.mapPath(file) + '.class')) - addParentDirectories(new RelativeArchivePath(zipEntry)) - remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) - } - } - - private void remapClass(FileCopyDetails fileCopyDetails) { - if (FilenameUtils.getExtension(fileCopyDetails.name) == 'class') { - InputStream is = fileCopyDetails.file.newInputStream() - try { - remapClass(is, fileCopyDetails.path, fileCopyDetails.lastModified) - } finally { - is.close() - } - } - } - - /** - * Applies remapping to the given class with the specified relocation path. The remapped class is then written - * to the zip file. classInputStream is closed automatically to prevent future file leaks. - * See #364 and #408. - */ - private void remapClass(InputStream classInputStream, String path, long lastModified) { - InputStream is = classInputStream - ClassReader cr = new ClassReader(is) - - // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. - // Copying the original constant pool should be avoided because it would keep references - // to the original class names. This is not a problem at runtime (because these entries in the - // constant pool are never used), but confuses some tools such as Felix' maven-bundle-plugin - // that use the constant pool to determine the dependencies of a class. - ClassWriter cw = new ClassWriter(0) - - ClassVisitor cv = new ClassRemapper(cw, remapper) - - try { - cr.accept(cv, ClassReader.EXPAND_FRAMES) - } catch (Throwable ise) { - throw new GradleException("Error in ASM processing class " + path, ise) - } finally { - is.close() - } - - byte[] renamedClass = cw.toByteArray() - - // Temporarily remove the multi-release prefix. - String multiReleasePrefix = path.find("^META-INF/versions/\\d+/") ?: "" - path = path.replace(multiReleasePrefix, "") - String mappedName = multiReleasePrefix + remapper.mapPath(path) - - InputStream bis = new ByteArrayInputStream(renamedClass) - try { - // Now we put it back on so the class file is written out with the right extension. - ZipEntry archiveEntry = new ZipEntry(mappedName + ".class") - archiveEntry.setTime(getArchiveTimeFor(lastModified)) - zipOutStr.putNextEntry(archiveEntry) - IOUtils.copyLarge(bis, zipOutStr) - zipOutStr.closeEntry() - } catch (ZipException ignored) { - log.warn("We have a duplicate " + mappedName + " in source project") - } finally { - bis.close() - } - } - - private void copyArchiveEntry(RelativeArchivePath archiveFile, ZipFile archive) { - String mappedPath = remapper.map(archiveFile.entry.name) - ZipEntry entry = new ZipEntry(mappedPath) - entry.setTime(getArchiveTimeFor(archiveFile.entry.time)) - RelativeArchivePath mappedFile = new RelativeArchivePath(entry) - addParentDirectories(mappedFile) - zipOutStr.putNextEntry(mappedFile.entry) - InputStream is = archive.getInputStream(archiveFile.entry) - try { - IOUtils.copyLarge(is, zipOutStr) - } finally { - is.close() - } - zipOutStr.closeEntry() - } - - @Override - protected void visitDir(FileCopyDetails dirDetails) { - try { - // Trailing slash in name indicates that entry is a directory - String path = dirDetails.relativePath.pathString + '/' - ZipEntry archiveEntry = new ZipEntry(path) - archiveEntry.setTime(getArchiveTimeFor(dirDetails.lastModified)) - archiveEntry.unixMode = (UnixStat.DIR_FLAG | dirDetails.permissions.toUnixNumeric()) - zipOutStr.putNextEntry(archiveEntry) - zipOutStr.closeEntry() - recordVisit(dirDetails.relativePath) - } catch (Exception e) { - throw new GradleException(String.format("Could not add %s to ZIP '%s'.", dirDetails, zipFile), e) - } - } - - private void transform(ArchiveFileTreeElement element, ZipFile archive) { - transformAndClose(element, archive.getInputStream(element.relativePath.entry)) - } - - private void transform(FileCopyDetails details) { - transformAndClose(details, details.file.newInputStream()) - } - - private void transformAndClose(FileTreeElement element, InputStream is) { - try { - String mappedPath = remapper.map(element.relativePath.pathString) - transformers.find { it.canTransformResource(element) }.transform( - TransformerContext.builder() - .path(mappedPath) - .inputStream(is) - .relocators(relocators) - .stats(stats) - .build() - ) - } finally { - is.close() - } - } - - private boolean isTransformable(FileTreeElement element) { - return transformers.any { it.canTransformResource(element) } - } - - } - - class RelativeArchivePath extends RelativePath { - - ZipEntry entry - - RelativeArchivePath(ZipEntry entry) { - super(!entry.directory, entry.name.split('/')) - this.entry = entry - } - - boolean isClassFile() { - return lastName.endsWith('.class') - } - - @Override - RelativeArchivePath getParent() { - if (!segments || segments.length == 1) { - return null - } else { - //Parent is always a directory so add / to the end of the path - String path = segments[0..-2].join('/') + '/' - return new RelativeArchivePath(setArchiveTimes(new ZipEntry(path))) - } - } - } - - class ArchiveFileTreeElement implements FileTreeElement { - - private final RelativeArchivePath archivePath - - ArchiveFileTreeElement(RelativeArchivePath archivePath) { - this.archivePath = archivePath - } - - boolean isClassFile() { - return archivePath.classFile - } - - @Override - File getFile() { - return null - } - - @Override - boolean isDirectory() { - return archivePath.entry.directory - } - - @Override - long getLastModified() { - return archivePath.entry.lastModifiedDate.time - } - - @Override - long getSize() { - return archivePath.entry.size - } - - @Override - InputStream open() { - return null - } - - @Override - void copyTo(OutputStream outputStream) { - - } - - @Override - boolean copyTo(File file) { - return false - } - - @Override - String getName() { - return archivePath.pathString - } - - @Override - String getPath() { - return archivePath.lastName - } - - @Override - RelativeArchivePath getRelativePath() { - return archivePath - } - - @Override - int getMode() { - return archivePath.entry.unixMode - } - - @Override - FilePermissions getPermissions() { - return new DefaultFilePermissions(getMode()) - } - - FileTreeElement asFileTreeElement() { - return new DefaultFileTreeElement(null, new RelativePath(!isDirectory(), archivePath.segments), null, null) - } - } -} From cf26f185d74062cb4ca74522a9f7e1bc369f23a2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 09:59:07 -0500 Subject: [PATCH 013/941] Kotlin migration: ShadowJar (#1029) --- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 344 +++++++++++++ .../shadow/internal/GradleVersionUtil.groovy | 26 - .../shadow/internal/RelocationUtil.groovy | 25 - .../plugins/shadow/tasks/ShadowJar.java | 450 ------------------ 4 files changed, 344 insertions(+), 501 deletions(-) create mode 100644 api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/RelocationUtil.groovy delete mode 100644 src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt new file mode 100644 index 000000000..912a9bf01 --- /dev/null +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -0,0 +1,344 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter +import com.github.jengelman.gradle.plugins.shadow.internal.DefaultZipCompressor +import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter +import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter +import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker +import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import java.util.concurrent.Callable +import java.util.jar.JarFile +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.Action +import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.file.FileCollection +import org.gradle.api.internal.DocumentationRegistry +import org.gradle.api.internal.file.FileResolver +import org.gradle.api.internal.file.copy.CopyAction +import org.gradle.api.internal.file.copy.DefaultCopySpec +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.bundling.Jar +import org.gradle.api.tasks.bundling.ZipEntryCompression +import org.gradle.api.tasks.util.PatternSet + +@CacheableTask +abstract class ShadowJar : + Jar(), + ShadowSpec { + private val _transformers = mutableListOf() + private val _relocators = mutableListOf() + private val _configurations = mutableListOf() + private val _stats = ShadowStats() + private val _includedDependencies = project.files(Callable { _dependencyFilter.resolve(_configurations) }) + + @Transient + private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) + + private var minimizeJar = false + private var _isEnableRelocation = false + private var _relocationPrefix = ShadowBasePlugin.SHADOW + private var _toMinimize: FileCollection? = null + private var _apiJars: FileCollection? = null + private var _sourceSetsClassesDirs: FileCollection? = null + + @Transient + private var _dependencyFilter: DependencyFilter = DefaultDependencyFilter(project) + + init { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) + + inputs.property("minimize") { minimizeJar } + outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { + _transformers.any { !isCacheableTransform(it::class.java) } || + _relocators.any { !isCacheableRelocator(it::class.java) } + } + } + + @get:Internal + override val stats: ShadowStats get() = _stats + + @get:Classpath + val toMinimize: FileCollection + get() { + if (_toMinimize == null) { + _toMinimize = if (minimizeJar) { + dependencyFilterForMinimize.resolve(_configurations) + .minus(apiJars) + } else { + project.objects.fileCollection() + } + } + return _toMinimize!! + } + + @get:Classpath + val apiJars: FileCollection + get() { + if (_apiJars == null) { + _apiJars = if (minimizeJar) { + UnusedTracker.getApiJarsFromProject(project) + } else { + project.objects.fileCollection() + } + } + return _apiJars!! + } + + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + val sourceSetsClassesDirs: FileCollection + get() { + if (_sourceSetsClassesDirs == null) { + val allClassesDirs = project.objects.fileCollection() + if (minimizeJar) { + project.extensions.getByType(SourceSetContainer::class.java).forEach { sourceSet -> + allClassesDirs.from(sourceSet.output.classesDirs) + } + } + _sourceSetsClassesDirs = allClassesDirs.filter { it.isDirectory } + } + return _sourceSetsClassesDirs!! + } + + @get:Classpath + val includedDependencies: FileCollection get() = _includedDependencies + + @get:Internal + val rootPatternSet: PatternSet + get() { + return (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet + } + + @get:Internal + val internalCompressor: ZipCompressor + get() { + return when (entryCompression) { + ZipEntryCompression.DEFLATED -> DefaultZipCompressor(isZip64, ZipOutputStream.DEFLATED) + ZipEntryCompression.STORED -> DefaultZipCompressor(isZip64, ZipOutputStream.STORED) + else -> throw IllegalArgumentException("Unknown Compression type $entryCompression") + } + } + + @get:Nested + var transformers: List + get() = _transformers + set(value) { + _transformers.clear() + _transformers.addAll(value) + } + + @get:Nested + var relocators: List + get() = _relocators + set(value) { + _relocators.clear() + _relocators.addAll(value) + } + + @get:Classpath + @get:Optional + var configurations: List + get() = _configurations + set(value) { + _configurations.clear() + _configurations.addAll(value) + } + + @get:Internal + var dependencyFilter: DependencyFilter + get() = _dependencyFilter + set(value) { + _dependencyFilter = value + } + + @get:Input + var isEnableRelocation: Boolean + get() = _isEnableRelocation + set(value) { + _isEnableRelocation = value + } + + @get:Input + var relocationPrefix: String + get() = _relocationPrefix + set(value) { + _relocationPrefix = value + } + + @Internal + override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest + + override fun minimize(): ShadowJar = apply { + minimizeJar = true + } + + override fun minimize(action: Action?): ShadowJar = apply { + minimize() + action?.execute(dependencyFilterForMinimize) + } + + override fun createCopyAction(): CopyAction { + val documentationRegistry = services.get(DocumentationRegistry::class.java) + val unusedTracker = if (minimizeJar) { + UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) + } else { + null + } + return ShadowCopyAction( + archiveFile.get().asFile, + internalCompressor, + documentationRegistry, + metadataCharset, + _transformers, + _relocators, + rootPatternSet, + _stats, + isPreserveFileTimestamps, + minimizeJar, + unusedTracker, + ) + } + + override fun dependencies(action: Action?): ShadowJar = apply { + action?.execute(_dependencyFilter) + } + + override fun transform(clazz: Class): ShadowJar { + return transform(clazz, null) + } + + override fun transform(clazz: Class, action: Action?): ShadowJar = apply { + val transformer = clazz.getDeclaredConstructor().newInstance() + addTransform(transformer, action) + } + + override fun transform(transformer: Transformer): ShadowJar = apply { + addTransform(transformer, null) + } + + override fun mergeServiceFiles(): ShadowJar { + return runCatching { + transform(ServiceFileTransformer::class.java, null) + }.getOrDefault(this) + } + + override fun mergeServiceFiles(rootPath: String): ShadowJar { + return runCatching { + transform(ServiceFileTransformer::class.java) { + it.setPath(rootPath) + } + }.getOrDefault(this) + } + + override fun mergeServiceFiles(action: Action?): ShadowJar { + return runCatching { + transform(ServiceFileTransformer::class.java, action) + }.getOrDefault(this) + } + + override fun mergeGroovyExtensionModules(): ShadowJar { + return runCatching { + transform(GroovyExtensionModuleTransformer::class.java, null) + }.getOrDefault(this) + } + + override fun append(resourcePath: String): ShadowJar { + return runCatching { + transform(AppendingTransformer::class.java) { + it.resource = resourcePath + } + }.getOrDefault(this) + } + + override fun relocate(pattern: String, destination: String): ShadowJar { + return relocate(pattern, destination, null) + } + + override fun relocate( + pattern: String, + destination: String, + action: Action?, + ): ShadowJar = apply { + val relocator = SimpleRelocator(pattern, destination, mutableListOf(), mutableListOf()) + addRelocator(relocator, action) + } + + override fun relocate(relocator: Relocator): ShadowJar = apply { + addRelocator(relocator, null) + } + + override fun relocate(clazz: Class): ShadowJar { + return relocate(clazz, null) + } + + override fun relocate(clazz: Class, action: Action?): ShadowJar = apply { + val relocator = clazz.getDeclaredConstructor().newInstance() + addRelocator(relocator, action) + } + + @TaskAction + override fun copy() { + if (_isEnableRelocation) { + configureRelocation() + } + from(_includedDependencies) + super.copy() + logger.info(_stats.toString()) + } + + private fun addRelocator(relocator: R, configure: Action?) { + configure?.execute(relocator) + _relocators.add(relocator) + } + + private fun addTransform(transformer: T, action: Action?) { + action?.execute(transformer) + _transformers.add(transformer) + } + + private fun isCacheableRelocator(clazz: Class): Boolean { + return clazz.isAnnotationPresent(CacheableRelocator::class.java) + } + + private fun isCacheableTransform(clazz: Class): Boolean { + return clazz.isAnnotationPresent(CacheableTransformer::class.java) + } + + private fun configureRelocation() { + val packages = mutableSetOf() + configurations.forEach { configuration -> + configuration.files.forEach { jarFile -> + JarFile(jarFile).use { jf -> + jf.entries().asSequence().forEach { entry -> + if (entry.name.endsWith(".class") && entry.name != "module-info.class") { + packages.add(entry.name.substringBeforeLast('/').replace('/', '.')) + } + } + } + } + } + packages.forEach { + relocate(it, "$_relocationPrefix.$it") + } + } +} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy deleted file mode 100644 index ae1a4fff6..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/GradleVersionUtil.groovy +++ /dev/null @@ -1,26 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import com.github.jengelman.gradle.plugins.shadow.tasks.ZipCompressor -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.internal.file.copy.CopySpecInternal -import org.gradle.api.tasks.bundling.Jar -import org.gradle.api.tasks.bundling.ZipEntryCompression -import org.gradle.api.tasks.util.PatternSet - -class GradleVersionUtil { - - static PatternSet getRootPatternSet(CopySpecInternal mainSpec) { - return mainSpec.buildRootResolver().getPatternSet() - } - - static ZipCompressor getInternalCompressor(ZipEntryCompression entryCompression, Jar jar) { - switch (entryCompression) { - case ZipEntryCompression.DEFLATED: - return new DefaultZipCompressor(jar.zip64, ZipOutputStream.DEFLATED) - case ZipEntryCompression.STORED: - return new DefaultZipCompressor(jar.zip64, ZipOutputStream.STORED) - default: - throw new IllegalArgumentException(String.format("Unknown Compression type %s", entryCompression)) - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/RelocationUtil.groovy b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/RelocationUtil.groovy deleted file mode 100644 index f94ab89ed..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/RelocationUtil.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import java.util.jar.JarFile - -class RelocationUtil { - - static void configureRelocation(ShadowJar target, String prefix) { - def packages = [] as Set - target.configurations.each { configuration -> - configuration.files.each { jar -> - JarFile jf = new JarFile(jar) - jf.entries().each { entry -> - if (entry.name.endsWith(".class") && entry.name != "module-info.class") { - packages << entry.name[0..entry.name.lastIndexOf('/') - 1].replaceAll('/', '.') - } - } - jf.close() - } - } - packages.each { - target.relocate(it, "${prefix}.${it}") - } - } -} diff --git a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java b/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java deleted file mode 100644 index bae7aa6ed..000000000 --- a/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.java +++ /dev/null @@ -1,450 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks; - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats; -import com.github.jengelman.gradle.plugins.shadow.internal.*; -import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator; -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator; -import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator; -import com.github.jengelman.gradle.plugins.shadow.transformers.*; -import org.gradle.api.Action; -import org.gradle.api.file.ConfigurableFileCollection; -import org.gradle.api.file.DuplicatesStrategy; -import org.gradle.api.file.FileCollection; -import org.gradle.api.internal.DocumentationRegistry; -import org.gradle.api.internal.file.FileResolver; -import org.gradle.api.internal.file.copy.CopyAction; -import org.gradle.api.tasks.*; -import org.gradle.api.tasks.bundling.Jar; -import org.gradle.api.tasks.util.PatternSet; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; - -@CacheableTask -public class ShadowJar extends Jar implements ShadowSpec { - - private List transformers; - private List relocators; - private List configurations; - private transient DependencyFilter dependencyFilter; - private boolean enableRelocation; - private String relocationPrefix = "shadow"; - private boolean minimizeJar; - private final transient DependencyFilter dependencyFilterForMinimize; - private FileCollection toMinimize; - private FileCollection apiJars; - private FileCollection sourceSetsClassesDirs; - - private final ShadowStats shadowStats = new ShadowStats(); - - private final ConfigurableFileCollection includedDependencies = getProject().files(new Callable() { - - @Override - public FileCollection call() { - return dependencyFilter.resolve(configurations); - } - }); - - public ShadowJar() { - super(); - setDuplicatesStrategy(DuplicatesStrategy.INCLUDE); //shadow filters out files later. This was the default behavior in Gradle < 6.x - dependencyFilter = new DefaultDependencyFilter(getProject()); - dependencyFilterForMinimize = new MinimizeDependencyFilter(getProject()); - setManifest(new DefaultInheritManifest(getServices().get(FileResolver.class))); - transformers = new ArrayList<>(); - relocators = new ArrayList<>(); - configurations = new ArrayList<>(); - - this.getInputs().property("minimize", (Callable) () -> minimizeJar); - this.getOutputs().doNotCacheIf("Has one or more transforms or relocators that are not cacheable", task -> { - for (Transformer transformer : transformers) { - if (!isCacheableTransform(transformer.getClass())) { - return true; - } - } - for (Relocator relocator : relocators) { - if (!isCacheableRelocator(relocator.getClass())) { - return true; - } - } - return false; - }); - } - - @Override - public ShadowJar minimize() { - minimizeJar = true; - return this; - } - - @Override - public ShadowJar minimize(Action c) { - minimize(); - if (c != null) { - c.execute(dependencyFilterForMinimize); - } - return this; - } - - @Override - @Internal - public ShadowStats getStats() { - return shadowStats; - } - - @Override - public InheritManifest getManifest() { - return (InheritManifest) super.getManifest(); - } - - @Override - @NotNull - protected CopyAction createCopyAction() { - DocumentationRegistry documentationRegistry = getServices().get(DocumentationRegistry.class); - final UnusedTracker unusedTracker = minimizeJar ? UnusedTracker.forProject(getApiJars(), getSourceSetsClassesDirs().getFiles(), getToMinimize()) : null; - return new ShadowCopyAction(getArchiveFile().get().getAsFile(), getInternalCompressor(), documentationRegistry, - this.getMetadataCharset(), transformers, relocators, getRootPatternSet(), shadowStats, - isPreserveFileTimestamps(), minimizeJar, unusedTracker); - } - - @Classpath - FileCollection getToMinimize() { - if (toMinimize == null) { - toMinimize = minimizeJar - ? dependencyFilterForMinimize.resolve(configurations).minus(getApiJars()) - : getProject().getObjects().fileCollection(); - } - return toMinimize; - } - - - @Classpath - FileCollection getApiJars() { - if (apiJars == null) { - apiJars = minimizeJar - ? UnusedTracker.getApiJarsFromProject(getProject()) - : getProject().getObjects().fileCollection(); - } - return apiJars; - } - - - @InputFiles - @PathSensitive(PathSensitivity.RELATIVE) - FileCollection getSourceSetsClassesDirs() { - if (sourceSetsClassesDirs == null) { - ConfigurableFileCollection allClassesDirs = getProject().getObjects().fileCollection(); - if (minimizeJar) { - for (SourceSet sourceSet : getProject().getExtensions().getByType(SourceSetContainer.class)) { - FileCollection classesDirs = sourceSet.getOutput().getClassesDirs(); - allClassesDirs.from(classesDirs); - } - } - sourceSetsClassesDirs = allClassesDirs.filter(File::isDirectory); - } - return sourceSetsClassesDirs; - } - - @Internal - protected ZipCompressor getInternalCompressor() { - return GradleVersionUtil.getInternalCompressor(getEntryCompression(), this); - } - - @TaskAction - @Override - protected void copy() { - if (enableRelocation) { - RelocationUtil.configureRelocation(this, relocationPrefix); - } - from(getIncludedDependencies()); - super.copy(); - getLogger().info(shadowStats.toString()); - } - - @Classpath - public FileCollection getIncludedDependencies() { - return includedDependencies; - } - - /** - * Utility method for assisting between changes in Gradle 1.12 and 2.x. - * - * @return this - */ - @Internal - protected PatternSet getRootPatternSet() { - return GradleVersionUtil.getRootPatternSet(getMainSpec()); - } - - /** - * Configure inclusion/exclusion of module and project dependencies into uber jar. - * - * @param c the configuration of the filter - * @return this - */ - @Override - public ShadowJar dependencies(Action c) { - if (c != null) { - c.execute(dependencyFilter); - } - return this; - } - - /** - * Add a Transformer instance for modifying JAR resources and configure. - * - * @param clazz the transformer to add. Must have a no-arg constructor - * @return this - */ - @Override - public ShadowJar transform(Class clazz) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return transform(clazz, null); - } - - /** - * Add a Transformer instance for modifying JAR resources and configure. - * - * @param clazz the transformer class to add. Must have no-arg constructor - * @param c the configuration for the transformer - * @return this - */ - @Override - public ShadowJar transform(Class clazz, Action c) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - T transformer = clazz.getDeclaredConstructor().newInstance(); - addTransform(transformer, c); - return this; - } - - private boolean isCacheableTransform(Class clazz) { - return clazz.isAnnotationPresent(CacheableTransformer.class); - } - - /** - * Add a preconfigured transformer instance. - * - * @param transformer the transformer instance to add - * @return this - */ - @Override - public ShadowJar transform(Transformer transformer) { - addTransform(transformer, null); - return this; - } - - private void addTransform(T transformer, Action c) { - if (c != null) { - c.execute(transformer); - } - - transformers.add(transformer); - } - - /** - * Syntactic sugar for merging service files in JARs. - * - * @return this - */ - @Override - public ShadowJar mergeServiceFiles() { - try { - transform(ServiceFileTransformer.class, null); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Syntactic sugar for merging service files in JARs. - * - * @return this - */ - @Override - public ShadowJar mergeServiceFiles(final String rootPath) { - try { - transform(ServiceFileTransformer.class, serviceFileTransformer -> serviceFileTransformer.setPath(rootPath)); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Syntactic sugar for merging service files in JARs. - * - * @return this - */ - @Override - public ShadowJar mergeServiceFiles(Action configureClosure) { - try { - transform(ServiceFileTransformer.class, configureClosure); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Syntactic sugar for merging Groovy extension module descriptor files in JARs - * - * @return this - */ - @Override - public ShadowSpec mergeGroovyExtensionModules() { - try { - transform(GroovyExtensionModuleTransformer.class, null); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Syntax sugar for merging service files in JARs - * - * @return this - */ - @Override - public ShadowJar append(final String resourcePath) { - try { - transform(AppendingTransformer.class, transformer -> transformer.setResource(resourcePath)); - } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException | - InstantiationException ignored) { - } - return this; - } - - /** - * Add a class relocator that maps each class in the pattern to the provided destination. - * - * @param pattern the source pattern to relocate - * @param destination the destination package - * @return this - */ - @Override - public ShadowJar relocate(String pattern, String destination) { - return relocate(pattern, destination, null); - } - - /** - * Add a class relocator that maps each class in the pattern to the provided destination. - * - * @param pattern the source pattern to relocate - * @param destination the destination package - * @param configure the configuration of the relocator - * @return this - */ - @Override - public ShadowJar relocate(String pattern, String destination, Action configure) { - SimpleRelocator relocator = new SimpleRelocator(pattern, destination, new ArrayList<>(), new ArrayList<>()); - addRelocator(relocator, configure); - return this; - } - - /** - * Add a relocator instance. - * - * @param relocator the relocator instance to add - * @return this - */ - @Override - public ShadowJar relocate(Relocator relocator) { - addRelocator(relocator, null); - return this; - } - - /** - * Add a relocator of the provided class. - * - * @param relocatorClass the relocator class to add. Must have a no-arg constructor. - * @return this - */ - @Override - public ShadowJar relocate(Class relocatorClass) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - return relocate(relocatorClass, null); - } - - private void addRelocator(R relocator, Action configure) { - if (configure != null) { - configure.execute(relocator); - } - - relocators.add(relocator); - } - - /** - * Add a relocator of the provided class and configure. - * - * @param relocatorClass the relocator class to add. Must have a no-arg constructor - * @param configure the configuration for the relocator - * @return this - */ - @Override - public ShadowJar relocate(Class relocatorClass, Action configure) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { - R relocator = relocatorClass.getDeclaredConstructor().newInstance(); - addRelocator(relocator, configure); - return this; - } - - private boolean isCacheableRelocator(Class relocatorClass) { - return relocatorClass.isAnnotationPresent(CacheableRelocator.class); - } - - @Nested - public List getTransformers() { - return this.transformers; - } - - public void setTransformers(List transformers) { - this.transformers = transformers; - } - - @Nested - public List getRelocators() { - return this.relocators; - } - - public void setRelocators(List relocators) { - this.relocators = relocators; - } - - @Classpath @Optional - public List getConfigurations() { - return this.configurations; - } - - public void setConfigurations(List configurations) { - this.configurations = configurations; - } - - @Internal - public DependencyFilter getDependencyFilter() { - return this.dependencyFilter; - } - - public void setDependencyFilter(DependencyFilter filter) { - this.dependencyFilter = filter; - } - - @Input - public boolean isEnableRelocation() { - return enableRelocation; - } - - public void setEnableRelocation(boolean enableRelocation) { - this.enableRelocation = enableRelocation; - } - - @Input - public String getRelocationPrefix() { - return relocationPrefix; - } - - public void setRelocationPrefix(String relocationPrefix) { - this.relocationPrefix = relocationPrefix; - } -} From 4cf33b1f2b4ec41df05858a06ee25405dfa84bd1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 10:05:00 -0500 Subject: [PATCH 014/941] Reformat Kotlin code to indent size 2 (#1030) --- api/build.gradle.kts | 2 +- .../plugins/shadow/ShadowApplicationPlugin.kt | 200 ++--- .../gradle/plugins/shadow/ShadowBasePlugin.kt | 36 +- .../gradle/plugins/shadow/ShadowExtension.kt | 10 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 156 ++-- .../gradle/plugins/shadow/ShadowPlugin.kt | 32 +- .../gradle/plugins/shadow/ShadowStats.kt | 94 +-- .../plugins/shadow/impl/RelocatorRemapper.kt | 74 +- .../internal/AbstractDependencyFilter.kt | 156 ++-- .../shadow/internal/CleanProperties.kt | 28 +- .../internal/DefaultDependencyFilter.kt | 22 +- .../shadow/internal/DefaultZipCompressor.kt | 24 +- .../shadow/internal/DependencyFilter.kt | 88 +-- .../plugins/shadow/internal/JavaJarExec.kt | 22 +- .../internal/MinimizeDependencyFilter.kt | 40 +- .../plugins/shadow/internal/UnusedTracker.kt | 124 ++-- .../gradle/plugins/shadow/internal/Utils.kt | 4 +- .../shadow/legacy/LegacyShadowPlugin.kt | 2 +- .../shadow/relocation/RelocateClassContext.kt | 26 +- .../shadow/relocation/RelocatePathContext.kt | 26 +- .../plugins/shadow/relocation/Relocator.kt | 16 +- .../shadow/relocation/SimpleRelocator.kt | 262 +++---- .../shadow/tasks/DefaultInheritManifest.kt | 58 +- .../plugins/shadow/tasks/InheritManifest.kt | 4 +- .../gradle/plugins/shadow/tasks/KnowsTask.kt | 22 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 694 +++++++++--------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 528 ++++++------- .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 82 +-- .../ApacheLicenseResourceTransformer.kt | 18 +- .../ApacheNoticeResourceTransformer.kt | 282 +++---- .../transformers/AppendingTransformer.kt | 58 +- .../ComponentsXmlResourceTransformer.kt | 192 ++--- .../DontIncludeResourceTransformer.kt | 14 +- .../GroovyExtensionModuleTransformer.kt | 128 ++-- .../IncludeResourceTransformer.kt | 26 +- .../Log4j2PluginsCacheFileTransformer.kt | 102 +-- .../ManifestAppenderTransformer.kt | 84 +-- .../ManifestResourceTransformer.kt | 100 +-- .../transformers/PropertiesFileTransformer.kt | 238 +++--- .../transformers/ServiceFileTransformer.kt | 140 ++-- .../shadow/transformers/Transformer.kt | 20 +- .../shadow/transformers/TransformerContext.kt | 54 +- .../transformers/XmlAppendingTransformer.kt | 88 +-- 43 files changed, 2188 insertions(+), 2188 deletions(-) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 7aa77ec18..964159f65 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -27,7 +27,7 @@ spotless { kotlin { ktlint().editorConfigOverride( mapOf( - "indent_size" to "4", + "indent_size" to "2", ), ) } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index a6b17a2c4..790cfc486 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -17,119 +17,119 @@ import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator import org.gradle.jvm.toolchain.JavaToolchainService abstract class ShadowApplicationPlugin : Plugin { - private lateinit var project: Project - private lateinit var javaApplication: JavaApplication + private lateinit var project: Project + private lateinit var javaApplication: JavaApplication - override fun apply(project: Project) { - this.project = project - this.javaApplication = project.extensions.getByType(JavaApplication::class.java) + override fun apply(project: Project) { + this.project = project + this.javaApplication = project.extensions.getByType(JavaApplication::class.java) - addRunTask() - addCreateScriptsTask() - configureDistSpec() - configureJarMainClass() - configureInstallTask() - } + addRunTask() + addCreateScriptsTask() + configureDistSpec() + configureJarMainClass() + configureInstallTask() + } - protected open fun configureJarMainClass() { - val classNameProvider = javaApplication.mainClass - shadowJar.configure { jar -> - jar.inputs.property("mainClassName", classNameProvider) - jar.doFirst { - jar.manifest.attributes["Main-Class"] = classNameProvider.get() - } - } + protected open fun configureJarMainClass() { + val classNameProvider = javaApplication.mainClass + shadowJar.configure { jar -> + jar.inputs.property("mainClassName", classNameProvider) + jar.doFirst { + jar.manifest.attributes["Main-Class"] = classNameProvider.get() + } } + } - protected open fun addRunTask() { - project.tasks.register(SHADOW_RUN_TASK_NAME, JavaJarExec::class.java) { - val install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) - it.dependsOn(install) - it.mainClass.set("-jar") - it.description = "Runs this project as a JVM application using the shadow jar" - it.group = ApplicationPlugin.APPLICATION_GROUP - it.conventionMapping.map("jvmArgs") { javaApplication.applicationDefaultJvmArgs } - it.jarFile.fileProvider( - project.providers.provider { - project.file("${install.get().destinationDir.path}/lib/${shadowJar.get().archiveFile.get().asFile.name}") - }, - ) - val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain - val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java) - .launcherFor(toolchain) - it.javaLauncher.set(defaultLauncher) - } + protected open fun addRunTask() { + project.tasks.register(SHADOW_RUN_TASK_NAME, JavaJarExec::class.java) { + val install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) + it.dependsOn(install) + it.mainClass.set("-jar") + it.description = "Runs this project as a JVM application using the shadow jar" + it.group = ApplicationPlugin.APPLICATION_GROUP + it.conventionMapping.map("jvmArgs") { javaApplication.applicationDefaultJvmArgs } + it.jarFile.fileProvider( + project.providers.provider { + project.file("${install.get().destinationDir.path}/lib/${shadowJar.get().archiveFile.get().asFile.name}") + }, + ) + val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain + val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java) + .launcherFor(toolchain) + it.javaLauncher.set(defaultLauncher) } + } - protected open fun addCreateScriptsTask() { - project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { - (it.unixStartScriptGenerator as TemplateBasedScriptGenerator).template = - project.resources.text.fromString(this::class.java.requireResourceAsText("internal/unixStartScript.txt")) - (it.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = - project.resources.text.fromString(this::class.java.requireResourceAsText("internal/windowsStartScript.txt")) - it.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" - it.group = ApplicationPlugin.APPLICATION_GROUP - it.classpath = project.files(shadowJar) - it.conventionMapping.map("mainClassName") { javaApplication.mainClass.get() } - it.conventionMapping.map("applicationName") { javaApplication.applicationName } - it.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile } - it.conventionMapping.map("defaultJvmOpts") { javaApplication.applicationDefaultJvmArgs } - it.inputs.files(project.files(shadowJar)) - } + protected open fun addCreateScriptsTask() { + project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { + (it.unixStartScriptGenerator as TemplateBasedScriptGenerator).template = + project.resources.text.fromString(this::class.java.requireResourceAsText("internal/unixStartScript.txt")) + (it.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = + project.resources.text.fromString(this::class.java.requireResourceAsText("internal/windowsStartScript.txt")) + it.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" + it.group = ApplicationPlugin.APPLICATION_GROUP + it.classpath = project.files(shadowJar) + it.conventionMapping.map("mainClassName") { javaApplication.mainClass.get() } + it.conventionMapping.map("applicationName") { javaApplication.applicationName } + it.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile } + it.conventionMapping.map("defaultJvmOpts") { javaApplication.applicationDefaultJvmArgs } + it.inputs.files(project.files(shadowJar)) } + } - protected open fun configureInstallTask() { - project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java).configure { task -> - val applicationName = project.providers.provider { javaApplication.applicationName } + protected open fun configureInstallTask() { + project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java).configure { task -> + val applicationName = project.providers.provider { javaApplication.applicationName } - task.doFirst { - if ( - !task.destinationDir.listFiles().isNullOrEmpty() && - ( - !task.destinationDir.resolve("lib").isDirectory || - !task.destinationDir.resolve("bin").isDirectory - ) - ) { - throw GradleException( - "The specified installation directory '${task.destinationDir}' is neither empty nor does it contain an installation for '${applicationName.get()}'.\n" + - "If you really want to install to this directory, delete it and run the install task again.\n" + - "Alternatively, choose a different installation directory.", - ) - } - } - task.doLast { - task.eachFile { - if (it.path == "bin/${applicationName.get()}") { - it.mode = 0x755 - } - } - } + task.doFirst { + if ( + !task.destinationDir.listFiles().isNullOrEmpty() && + ( + !task.destinationDir.resolve("lib").isDirectory || + !task.destinationDir.resolve("bin").isDirectory + ) + ) { + throw GradleException( + "The specified installation directory '${task.destinationDir}' is neither empty nor does it contain an installation for '${applicationName.get()}'.\n" + + "If you really want to install to this directory, delete it and run the install task again.\n" + + "Alternatively, choose a different installation directory.", + ) + } + } + task.doLast { + task.eachFile { + if (it.path == "bin/${applicationName.get()}") { + it.mode = 0x755 + } } + } } + } - protected open fun configureDistSpec() { - project.extensions.getByType(DistributionContainer::class.java) - .register(ShadowBasePlugin.DISTRIBUTION_NAME) { distributions -> - distributions.contents { contents -> - contents.from(project.file("src/dist")) - contents.into("lib") { lib -> - lib.from(shadowJar) - lib.from(project.configurations.named(ShadowBasePlugin.CONFIGURATION_NAME)) - } - contents.into("bin") { bin -> - bin.from(project.tasks.named(SHADOW_SCRIPTS_TASK_NAME)) - bin.filePermissions { it.unix(493) } - } - } - } - } + protected open fun configureDistSpec() { + project.extensions.getByType(DistributionContainer::class.java) + .register(ShadowBasePlugin.DISTRIBUTION_NAME) { distributions -> + distributions.contents { contents -> + contents.from(project.file("src/dist")) + contents.into("lib") { lib -> + lib.from(shadowJar) + lib.from(project.configurations.named(ShadowBasePlugin.CONFIGURATION_NAME)) + } + contents.into("bin") { bin -> + bin.from(project.tasks.named(SHADOW_SCRIPTS_TASK_NAME)) + bin.filePermissions { it.unix(493) } + } + } + } + } - protected val shadowJar: TaskProvider - get() = project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar::class.java) + protected val shadowJar: TaskProvider + get() = project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar::class.java) - companion object { - const val SHADOW_RUN_TASK_NAME: String = "runShadow" - const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" - const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" - } + companion object { + const val SHADOW_RUN_TASK_NAME: String = "runShadow" + const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" + const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 94c8a051e..c095ae9c3 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -8,24 +8,24 @@ import org.gradle.util.GradleVersion abstract class ShadowBasePlugin : Plugin { - override fun apply(project: Project) { - if (GradleVersion.current() < GradleVersion.version("8.3")) { - throw GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.") - } - project.extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) - project.configurations.create(CONFIGURATION_NAME) - project.tasks.register(KnowsTask.NAME, KnowsTask::class.java) { knows -> - knows.group = GROUP_NAME - knows.description = KnowsTask.DESC - } + override fun apply(project: Project) { + if (GradleVersion.current() < GradleVersion.version("8.3")) { + throw GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.") } - - companion object { - const val SHADOW: String = "shadow" - const val GROUP_NAME: String = SHADOW - const val EXTENSION_NAME: String = SHADOW - const val CONFIGURATION_NAME: String = SHADOW - const val COMPONENT_NAME: String = SHADOW - const val DISTRIBUTION_NAME: String = SHADOW + project.extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) + project.configurations.create(CONFIGURATION_NAME) + project.tasks.register(KnowsTask.NAME, KnowsTask::class.java) { knows -> + knows.group = GROUP_NAME + knows.description = KnowsTask.DESC } + } + + companion object { + const val SHADOW: String = "shadow" + const val GROUP_NAME: String = SHADOW + const val EXTENSION_NAME: String = SHADOW + const val CONFIGURATION_NAME: String = SHADOW + const val COMPONENT_NAME: String = SHADOW + const val DISTRIBUTION_NAME: String = SHADOW + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index 6b22883a1..871f15d7b 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -5,10 +5,10 @@ import org.gradle.api.publish.maven.MavenPublication @Deprecated("This is deprecated since 8.3.2") abstract class ShadowExtension(project: Project) { - private val components = project.components + private val components = project.components - @Deprecated("configure publication using component.shadow directly.") - fun component(publication: MavenPublication) { - publication.from(components.findByName(ShadowBasePlugin.COMPONENT_NAME)) - } + @Deprecated("configure publication using component.shadow directly.") + fun component(publication: MavenPublication) { + publication.from(components.findByName(ShadowBasePlugin.COMPONENT_NAME)) + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt index 5eb6fecd5..35dfdf3b7 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -18,94 +18,94 @@ import org.gradle.jvm.tasks.Jar import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin abstract class ShadowJavaPlugin @Inject constructor( - private val softwareComponentFactory: SoftwareComponentFactory, + private val softwareComponentFactory: SoftwareComponentFactory, ) : Plugin { - override fun apply(project: Project) { - val shadowConfiguration = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) - val shadowTaskProvider = configureShadowTask(project, shadowConfiguration) + override fun apply(project: Project) { + val shadowConfiguration = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) + val shadowTaskProvider = configureShadowTask(project, shadowConfiguration) - project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { - it.extendsFrom(shadowConfiguration) - } + project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { + it.extendsFrom(shadowConfiguration) + } - val shadowRuntimeElements = project.configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { - it.extendsFrom(shadowConfiguration) - it.isCanBeConsumed = true - it.isCanBeResolved = false - it.attributes { attr -> - attr.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) - attr.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category::class.java, Category.LIBRARY)) - attr.attribute( - LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, - project.objects.named(LibraryElements::class.java, LibraryElements.JAR), - ) - attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling::class.java, Bundling.SHADOWED)) - } - it.outgoing.artifact(shadowTaskProvider) - } + val shadowRuntimeElements = project.configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { + it.extendsFrom(shadowConfiguration) + it.isCanBeConsumed = true + it.isCanBeResolved = false + it.attributes { attr -> + attr.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) + attr.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category::class.java, Category.LIBRARY)) + attr.attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + project.objects.named(LibraryElements::class.java, LibraryElements.JAR), + ) + attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling::class.java, Bundling.SHADOWED)) + } + it.outgoing.artifact(shadowTaskProvider) + } - project.components.named("java", AdhocComponentWithVariants::class.java) { - it.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> - variant.mapToOptional() - } - } + project.components.named("java", AdhocComponentWithVariants::class.java) { + it.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> + variant.mapToOptional() + } + } - val shadowComponent = softwareComponentFactory.adhoc(ShadowBasePlugin.COMPONENT_NAME) - project.components.add(shadowComponent) - shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> - variant.mapToMavenScope("runtime") - } + val shadowComponent = softwareComponentFactory.adhoc(ShadowBasePlugin.COMPONENT_NAME) + project.components.add(shadowComponent) + shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> + variant.mapToMavenScope("runtime") + } - project.plugins.withType(JavaGradlePluginPlugin::class.java).configureEach { - // Remove the gradleApi so it isn't merged into the jar file. - // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. - // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/de - project.configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { - it.dependencies.remove(project.dependencies.gradleApi()) - } - // Compile only gradleApi() to make sure the plugin can compile against Gradle API. - project.configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) { - it.dependencies.add(project.dependencies.gradleApi()) - } - } + project.plugins.withType(JavaGradlePluginPlugin::class.java).configureEach { + // Remove the gradleApi so it isn't merged into the jar file. + // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. + // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/de + project.configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { + it.dependencies.remove(project.dependencies.gradleApi()) + } + // Compile only gradleApi() to make sure the plugin can compile against Gradle API. + project.configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) { + it.dependencies.add(project.dependencies.gradleApi()) + } } + } - private fun configureShadowTask(project: Project, shadowConfiguration: Configuration): TaskProvider { - val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) - val jarTask = project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar::class.java) - val taskProvider = project.tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { shadow -> - shadow.group = ShadowBasePlugin.GROUP_NAME - shadow.description = "Create a combined JAR of project and runtime dependencies" - shadow.archiveClassifier.set("all") - shadow.manifest.inheritFrom(jarTask.get().manifest) - val attrProvider = jarTask.map { it.manifest.attributes["Class-Path"]?.toString().orEmpty() } - val files = project.objects.fileCollection().from(shadowConfiguration) - shadow.doFirst { - if (!files.isEmpty) { - val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } - shadow.manifest.attributes["Class-Path"] = attrs.joinToString(" ").trim() - } - } - shadow.from(sourceSets.getByName("main").output) - shadow.configurations = listOf( - project.configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) - ?: project.configurations.getByName("runtime"), - ) - shadow.exclude( - "META-INF/INDEX.LIST", - "META-INF/*.SF", - "META-INF/*.DSA", - "META-INF/*.RSA", - "module-info.class", - ) + private fun configureShadowTask(project: Project, shadowConfiguration: Configuration): TaskProvider { + val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) + val jarTask = project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar::class.java) + val taskProvider = project.tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { shadow -> + shadow.group = ShadowBasePlugin.GROUP_NAME + shadow.description = "Create a combined JAR of project and runtime dependencies" + shadow.archiveClassifier.set("all") + shadow.manifest.inheritFrom(jarTask.get().manifest) + val attrProvider = jarTask.map { it.manifest.attributes["Class-Path"]?.toString().orEmpty() } + val files = project.objects.fileCollection().from(shadowConfiguration) + shadow.doFirst { + if (!files.isEmpty) { + val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } + shadow.manifest.attributes["Class-Path"] = attrs.joinToString(" ").trim() } - project.artifacts.add(shadowConfiguration.name, taskProvider) - return taskProvider + } + shadow.from(sourceSets.getByName("main").output) + shadow.configurations = listOf( + project.configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) + ?: project.configurations.getByName("runtime"), + ) + shadow.exclude( + "META-INF/INDEX.LIST", + "META-INF/*.SF", + "META-INF/*.DSA", + "META-INF/*.RSA", + "module-info.class", + ) } + project.artifacts.add(shadowConfiguration.name, taskProvider) + return taskProvider + } - companion object { - const val SHADOW_JAR_TASK_NAME: String = "shadowJar" - const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" - } + companion object { + const val SHADOW_JAR_TASK_NAME: String = "shadowJar" + const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt index 0b5c58e3c..93fce387e 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt @@ -8,21 +8,21 @@ import org.gradle.api.plugins.JavaPlugin abstract class ShadowPlugin : Plugin { - override fun apply(project: Project) { - project.run { - plugins.apply(ShadowBasePlugin::class.java) - plugins.withType(JavaPlugin::class.java) { - plugins.apply(ShadowJavaPlugin::class.java) - } - plugins.withType(ApplicationPlugin::class.java) { - plugins.apply(ShadowApplicationPlugin::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. - // If the user applies shadow before those plugins. However, this is fine, because this was also - // the behavior with the old plugin when applying in that order. - plugins.apply(LegacyShadowPlugin::class.java) - } + override fun apply(project: Project) { + project.run { + plugins.apply(ShadowBasePlugin::class.java) + plugins.withType(JavaPlugin::class.java) { + plugins.apply(ShadowJavaPlugin::class.java) + } + plugins.withType(ApplicationPlugin::class.java) { + plugins.apply(ShadowApplicationPlugin::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. + // If the user applies shadow before those plugins. However, this is fine, because this was also + // the behavior with the old plugin when applying in that order. + plugins.apply(LegacyShadowPlugin::class.java) } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt index 01cc1b761..f59d9a5fd 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt @@ -3,63 +3,63 @@ package com.github.jengelman.gradle.plugins.shadow import org.gradle.api.GradleException open class ShadowStats { - open var totalTime: Long = 0 - open var jarStartTime: Long = 0 - open var jarEndTime: Long = 0 - open var jarCount: Int = 1 - open var processingJar: Boolean = false - open val relocations: MutableMap = mutableMapOf() + open var totalTime: Long = 0 + open var jarStartTime: Long = 0 + open var jarEndTime: Long = 0 + open var jarCount: Int = 1 + open var processingJar: Boolean = false + open val relocations: MutableMap = mutableMapOf() - open val relocationString: String - get() { - return relocations.map { (k, v) -> "$k → $v" } - .sorted() - .joinToString("\n") - } + open val relocationString: String + get() { + return relocations.map { (k, v) -> "$k → $v" } + .sorted() + .joinToString("\n") + } - open val jarTiming: Long - get() = jarEndTime - jarStartTime + open val jarTiming: Long + get() = jarEndTime - jarStartTime - open val totalTimeSecs: Double - get() = totalTime / 1000.0 + open val totalTimeSecs: Double + get() = totalTime / 1000.0 - open val averageTimePerJar: Double - get() = totalTime / jarCount.toDouble() + open val averageTimePerJar: Double + get() = totalTime / jarCount.toDouble() - open val averageTimeSecsPerJar: Double - get() = averageTimePerJar / 1000.0 + open val averageTimeSecsPerJar: Double + get() = averageTimePerJar / 1000.0 - open val buildScanData: Map - get() = mapOf( - "dependencies" to jarCount.toString(), - "relocations" to relocationString, - ) + open val buildScanData: Map + get() = mapOf( + "dependencies" to jarCount.toString(), + "relocations" to relocationString, + ) - open fun relocate(src: String, dst: String) { - relocations[src] = dst - } + open fun relocate(src: String, dst: String) { + relocations[src] = dst + } - open fun startJar() { - if (processingJar) throw GradleException("Can only time one entry at a time") - processingJar = true - jarStartTime = System.currentTimeMillis() - } + open fun startJar() { + if (processingJar) throw GradleException("Can only time one entry at a time") + processingJar = true + jarStartTime = System.currentTimeMillis() + } - open fun finishJar() { - if (processingJar) { - jarEndTime = System.currentTimeMillis() - jarCount++ - totalTime += jarTiming - processingJar = false - } + open fun finishJar() { + if (processingJar) { + jarEndTime = System.currentTimeMillis() + jarCount++ + totalTime += jarTiming + processingJar = false } + } - open fun printStats() { - println(this) - } + open fun printStats() { + println(this) + } - override fun toString(): String { - return """ + override fun toString(): String { + return """ ******************* GRADLE SHADOW STATS @@ -67,6 +67,6 @@ open class ShadowStats { Total Time: ${totalTimeSecs}s [${totalTime}ms] Average Time/Jar: ${averageTimeSecsPerJar}s [${averageTimePerJar}ms] ******************* - """.trimIndent() - } + """.trimIndent() + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt index be5353bf8..fc0335803 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt @@ -14,51 +14,51 @@ import org.objectweb.asm.commons.Remapper * @author John Engelman */ open class RelocatorRemapper( - private val relocators: List, - private val stats: ShadowStats, + private val relocators: List, + private val stats: ShadowStats, ) : Remapper() { - private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") + private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") - open fun hasRelocators(): Boolean = relocators.isNotEmpty() + open fun hasRelocators(): Boolean = relocators.isNotEmpty() - override fun mapValue(value: Any): Any { - return if (value is String) { - map(value) - } else { - super.mapValue(value) - } + override fun mapValue(value: Any): Any { + return if (value is String) { + map(value) + } else { + super.mapValue(value) } + } - override fun map(name: String): String { - var newName = name - var prefix = "" - var suffix = "" + override fun map(name: String): String { + var newName = name + var prefix = "" + var suffix = "" - val matcher = classPattern.matcher(newName) - if (matcher.matches()) { - prefix = matcher.group(1) + "L" - suffix = "" - newName = matcher.group(2) - } - - for (relocator in relocators) { - if (relocator.canRelocateClass(newName)) { - val classContext = RelocateClassContext.builder().className(newName).stats(stats).build() - return prefix + relocator.relocateClass(classContext) + suffix - } else if (relocator.canRelocatePath(newName)) { - val pathContext = RelocatePathContext.builder().path(newName).stats(stats).build() - return prefix + relocator.relocatePath(pathContext) + suffix - } - } - - return name + val matcher = classPattern.matcher(newName) + if (matcher.matches()) { + prefix = matcher.group(1) + "L" + suffix = "" + newName = matcher.group(2) } - open fun mapPath(path: String): String { - return map(path.substring(0, path.indexOf('.'))) + for (relocator in relocators) { + if (relocator.canRelocateClass(newName)) { + val classContext = RelocateClassContext.builder().className(newName).stats(stats).build() + return prefix + relocator.relocateClass(classContext) + suffix + } else if (relocator.canRelocatePath(newName)) { + val pathContext = RelocatePathContext.builder().path(newName).stats(stats).build() + return prefix + relocator.relocatePath(pathContext) + suffix + } } - open fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String { - return mapPath(path.pathString) - } + return name + } + + open fun mapPath(path: String): String { + return map(path.substring(0, path.indexOf('.'))) + } + + open fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String { + return mapPath(path.pathString) + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt index 087e27930..74144effe 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -11,95 +11,95 @@ import org.gradle.api.specs.Spec import org.gradle.api.specs.Specs internal sealed class AbstractDependencyFilter( - private val project: Project, + private val project: Project, ) : DependencyFilter { - protected val includeSpecs: MutableList> = mutableListOf() - protected val excludeSpecs: MutableList> = mutableListOf() + protected val includeSpecs: MutableList> = mutableListOf() + protected val excludeSpecs: MutableList> = mutableListOf() - protected abstract fun resolve( - dependencies: Set, - includedDependencies: MutableSet, - excludedDependencies: MutableSet, - ) + protected abstract fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) - override fun resolve(configuration: FileCollection): FileCollection { - val includedDeps = mutableSetOf() - val excludedDeps = mutableSetOf() - configuration as Configuration - resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, includedDeps, excludedDeps) - return project.files(configuration.files) - - project.files(excludedDeps.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) - } + override fun resolve(configuration: FileCollection): FileCollection { + val includedDeps = mutableSetOf() + val excludedDeps = mutableSetOf() + configuration as Configuration + resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, includedDeps, excludedDeps) + return project.files(configuration.files) - + project.files(excludedDeps.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) + } - override fun resolve(configurations: Collection): FileCollection { - return configurations.map { resolve(it) } - .reduceOrNull { acc, fileCollection -> acc + fileCollection } - ?: project.files() - } + override fun resolve(configurations: Collection): FileCollection { + return configurations.map { resolve(it) } + .reduceOrNull { acc, fileCollection -> acc + fileCollection } + ?: project.files() + } - /** - * Exclude dependencies that match the provided spec. - */ - override fun exclude(spec: Spec): DependencyFilter = apply { - excludeSpecs.add(spec) - } + /** + * Exclude dependencies that match the provided spec. + */ + override fun exclude(spec: Spec): DependencyFilter = apply { + excludeSpecs.add(spec) + } - /** - * Include dependencies that match the provided spec. - */ - override fun include(spec: Spec): DependencyFilter = apply { - includeSpecs.add(spec) - } + /** + * Include dependencies that match the provided spec. + */ + override fun include(spec: Spec): DependencyFilter = apply { + includeSpecs.add(spec) + } - /** - * Create a spec that matches the provided project notation on group, name, and version. - */ - override fun project(notation: Map): Spec { - return dependency(dependency = project.dependencies.project(notation)) - } + /** + * Create a spec that matches the provided project notation on group, name, and version. + */ + override fun project(notation: Map): Spec { + return dependency(dependency = project.dependencies.project(notation)) + } - /** - * Create a spec that matches the default configuration for the provided project path on group, name, and version. - */ - override fun project(notation: String): Spec { - return dependency( - dependency = project.dependencies.project( - mapOf( - "path" to notation, - "configuration" to "default", - ), - ), - ) - } + /** + * Create a spec that matches the default configuration for the provided project path on group, name, and version. + */ + override fun project(notation: String): Spec { + return dependency( + dependency = project.dependencies.project( + mapOf( + "path" to notation, + "configuration" to "default", + ), + ), + ) + } - /** - * Create a spec that matches dependencies using the provided notation on group, name, and version. - */ - override fun dependency(notation: Any): Spec { - return dependency(dependency = project.dependencies.create(notation)) - } + /** + * Create a spec that matches dependencies using the provided notation on group, name, and version. + */ + override fun dependency(notation: Any): Spec { + return dependency(dependency = project.dependencies.create(notation)) + } - /** - * Create a spec that matches the provided dependency on group, name, and version. - */ - override fun dependency(dependency: Dependency): Spec { - return Spec { resolvedDependency -> - (dependency.group == null || resolvedDependency.moduleGroup.matches(dependency.group!!.toRegex())) && - resolvedDependency.moduleName.matches(dependency.name.toRegex()) && - (dependency.version == null || resolvedDependency.moduleVersion.matches(dependency.version!!.toRegex())) - } + /** + * Create a spec that matches the provided dependency on group, name, and version. + */ + override fun dependency(dependency: Dependency): Spec { + return Spec { resolvedDependency -> + (dependency.group == null || resolvedDependency.moduleGroup.matches(dependency.group!!.toRegex())) && + resolvedDependency.moduleName.matches(dependency.name.toRegex()) && + (dependency.version == null || resolvedDependency.moduleVersion.matches(dependency.version!!.toRegex())) } + } - /** - * Create a spec that matches the provided closure. - */ - override fun dependency(closure: Closure<*>): Spec { - return Specs.convertClosureToSpec(closure) - } + /** + * Create a spec that matches the provided closure. + */ + override fun dependency(closure: Closure<*>): Spec { + return Specs.convertClosureToSpec(closure) + } - protected fun ResolvedDependency.isIncluded(): Boolean { - val include = includeSpecs.isEmpty() || includeSpecs.any { it.isSatisfiedBy(this) } - val exclude = excludeSpecs.isNotEmpty() && excludeSpecs.any { it.isSatisfiedBy(this) } - return include && !exclude - } + protected fun ResolvedDependency.isIncluded(): Boolean { + val include = includeSpecs.isEmpty() || includeSpecs.any { it.isSatisfiedBy(this) } + val exclude = excludeSpecs.isNotEmpty() && excludeSpecs.any { it.isSatisfiedBy(this) } + return include && !exclude + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt index 3f6be3d9b..13f6730f7 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt @@ -7,22 +7,22 @@ import java.util.Date import java.util.Properties internal class CleanProperties : Properties() { - @Throws(IOException::class) - override fun store(writer: Writer, comments: String) { - super.store(StripCommentsWithTimestampBufferedWriter(writer), comments) - } + @Throws(IOException::class) + override fun store(writer: Writer, comments: String) { + super.store(StripCommentsWithTimestampBufferedWriter(writer), comments) + } - private class StripCommentsWithTimestampBufferedWriter(out: Writer) : BufferedWriter(out) { - private val lengthOfExpectedTimestamp = ("#" + Date().toString()).length + private class StripCommentsWithTimestampBufferedWriter(out: Writer) : BufferedWriter(out) { + private val lengthOfExpectedTimestamp = ("#" + Date().toString()).length - @Throws(IOException::class) - override fun write(str: String) { - if (str.couldBeCommentWithTimestamp) return - super.write(str) - } + @Throws(IOException::class) + override fun write(str: String) { + if (str.couldBeCommentWithTimestamp) return + super.write(str) + } - private val String?.couldBeCommentWithTimestamp: Boolean get() { - return this != null && startsWith("#") && length == lengthOfExpectedTimestamp - } + private val String?.couldBeCommentWithTimestamp: Boolean get() { + return this != null && startsWith("#") && length == lengthOfExpectedTimestamp } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt index 25604eaf2..b10468286 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt @@ -4,17 +4,17 @@ import org.gradle.api.Project import org.gradle.api.artifacts.ResolvedDependency internal class DefaultDependencyFilter( - project: Project, + project: Project, ) : AbstractDependencyFilter(project) { - override fun resolve( - dependencies: Set, - includedDependencies: MutableSet, - excludedDependencies: MutableSet, - ) { - dependencies.forEach { - if (if (it.isIncluded()) includedDependencies.add(it) else excludedDependencies.add(it)) { - resolve(it.children, includedDependencies, excludedDependencies) - } - } + override fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) { + dependencies.forEach { + if (if (it.isIncluded()) includedDependencies.add(it) else excludedDependencies.add(it)) { + resolve(it.children, includedDependencies, excludedDependencies) + } } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt index 244bccd84..d002cb2b4 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt @@ -7,19 +7,19 @@ import org.apache.tools.zip.ZipOutputStream import org.gradle.api.UncheckedIOException internal class DefaultZipCompressor( - allowZip64Mode: Boolean, - private val entryCompressionMethod: Int, + allowZip64Mode: Boolean, + private val entryCompressionMethod: Int, ) : ZipCompressor { - private val zip64Mode = if (allowZip64Mode) Zip64Mode.AsNeeded else Zip64Mode.Never + private val zip64Mode = if (allowZip64Mode) Zip64Mode.AsNeeded else Zip64Mode.Never - override fun createArchiveOutputStream(destination: File): ZipOutputStream { - try { - return ZipOutputStream(destination).apply { - setUseZip64(zip64Mode) - setMethod(entryCompressionMethod) - } - } catch (e: Exception) { - throw UncheckedIOException("Unable to create ZIP output stream for file $destination.", e) - } + override fun createArchiveOutputStream(destination: File): ZipOutputStream { + try { + return ZipOutputStream(destination).apply { + setUseZip64(zip64Mode) + setMethod(entryCompressionMethod) + } + } catch (e: Exception) { + throw UncheckedIOException("Unable to create ZIP output stream for file $destination.", e) } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt index 993165cf0..b6ddff4b3 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt @@ -7,48 +7,48 @@ import org.gradle.api.file.FileCollection import org.gradle.api.specs.Spec interface DependencyFilter { - /** - * Resolve a FileCollection against the include/exclude rules in the filter. - */ - fun resolve(configuration: FileCollection): FileCollection - - /** - * Resolve all FileCollections against the include/exclude rules in the filter and combine the results. - */ - fun resolve(configurations: Collection): FileCollection - - /** - * Exclude dependencies that match the provided spec. - */ - fun exclude(spec: Spec): DependencyFilter - - /** - * Include dependencies that match the provided spec. - */ - fun include(spec: Spec): DependencyFilter - - /** - * Create a spec that matches the provided project notation on group, name, and version. - */ - fun project(notation: Map): Spec - - /** - * Create a spec that matches the default configuration for the provided project path on group, name, and version. - */ - fun project(notation: String): Spec - - /** - * Create a spec that matches dependencies using the provided notation on group, name, and version. - */ - fun dependency(notation: Any): Spec - - /** - * Create a spec that matches the provided dependency on group, name, and version. - */ - fun dependency(dependency: Dependency): Spec - - /** - * Create a spec that matches the provided closure. - */ - fun dependency(closure: Closure<*>): Spec + /** + * Resolve a FileCollection against the include/exclude rules in the filter. + */ + fun resolve(configuration: FileCollection): FileCollection + + /** + * Resolve all FileCollections against the include/exclude rules in the filter and combine the results. + */ + fun resolve(configurations: Collection): FileCollection + + /** + * Exclude dependencies that match the provided spec. + */ + fun exclude(spec: Spec): DependencyFilter + + /** + * Include dependencies that match the provided spec. + */ + fun include(spec: Spec): DependencyFilter + + /** + * Create a spec that matches the provided project notation on group, name, and version. + */ + fun project(notation: Map): Spec + + /** + * Create a spec that matches the default configuration for the provided project path on group, name, and version. + */ + fun project(notation: String): Spec + + /** + * Create a spec that matches dependencies using the provided notation on group, name, and version. + */ + fun dependency(notation: Any): Spec + + /** + * Create a spec that matches the provided dependency on group, name, and version. + */ + fun dependency(dependency: Dependency): Spec + + /** + * Create a spec that matches the provided closure. + */ + fun dependency(closure: Closure<*>): Spec } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt index 316320d82..3f62f20a3 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt @@ -6,17 +6,17 @@ import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.TaskAction internal abstract class JavaJarExec : JavaExec() { - @get:InputFile - abstract val jarFile: RegularFileProperty + @get:InputFile + abstract val jarFile: RegularFileProperty - @TaskAction - override fun exec() { - val allArgs = buildList { - add(jarFile.get().asFile.path) - // Must cast args to List here to avoid type mismatch. - addAll(args as List) - } - setArgs(allArgs) - super.exec() + @TaskAction + override fun exec() { + val allArgs = buildList { + add(jarFile.get().asFile.path) + // Must cast args to List here to avoid type mismatch. + addAll(args as List) } + setArgs(allArgs) + super.exec() + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt index df6bf5bc2..44b4d3a55 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt @@ -4,27 +4,27 @@ import org.gradle.api.Project import org.gradle.api.artifacts.ResolvedDependency internal class MinimizeDependencyFilter( - project: Project, + project: Project, ) : AbstractDependencyFilter(project) { - override fun resolve( - dependencies: Set, - includedDependencies: MutableSet, - excludedDependencies: MutableSet, - ) { - dependencies.forEach { - if (it.isIncluded() && !isParentExcluded(excludedDependencies, it)) { - includedDependencies.add(it) - } else { - excludedDependencies.add(it) - } - resolve(it.children, includedDependencies, excludedDependencies) - } + override fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) { + dependencies.forEach { + if (it.isIncluded() && !isParentExcluded(excludedDependencies, it)) { + includedDependencies.add(it) + } else { + excludedDependencies.add(it) + } + resolve(it.children, includedDependencies, excludedDependencies) } + } - private fun isParentExcluded( - excludedDependencies: Set, - dependency: ResolvedDependency, - ): Boolean { - return excludedDependencies.any { it in dependency.parents } - } + private fun isParentExcluded( + excludedDependencies: Set, + dependency: ResolvedDependency, + ): Boolean { + return excludedDependencies.any { it in dependency.parents } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index 504459d60..269f1b15a 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -13,79 +13,79 @@ import org.vafer.jdependency.ClazzpathUnit /** Tracks unused classes in the project classpath. */ internal class UnusedTracker private constructor( - classDirs: Iterable, - classJars: FileCollection, - private val _toMinimize: FileCollection, + classDirs: Iterable, + classJars: FileCollection, + private val _toMinimize: FileCollection, ) { - private val projectUnits: List - private val cp = Clazzpath() + private val projectUnits: List + private val cp = Clazzpath() - init { - projectUnits = classDirs.map { cp.addClazzpathUnit(it) } + classJars.map { cp.addClazzpathUnit(it) } - } + init { + projectUnits = classDirs.map { cp.addClazzpathUnit(it) } + classJars.map { cp.addClazzpathUnit(it) } + } - @get:InputFiles - val toMinimize: FileCollection get() = _toMinimize + @get:InputFiles + val toMinimize: FileCollection get() = _toMinimize - fun findUnused(): Set { - val unused = cp.clazzes.toMutableSet() - for (cpu in projectUnits) { - unused.removeAll(cpu.clazzes) - unused.removeAll(cpu.transitiveDependencies) - } - return unused.map { it.name }.toSet() + fun findUnused(): Set { + val unused = cp.clazzes.toMutableSet() + for (cpu in projectUnits) { + unused.removeAll(cpu.clazzes) + unused.removeAll(cpu.transitiveDependencies) } + return unused.map { it.name }.toSet() + } - fun addDependency(jarOrDir: File) { - if (_toMinimize.contains(jarOrDir)) { - cp.addClazzpathUnit(jarOrDir) - } + fun addDependency(jarOrDir: File) { + if (_toMinimize.contains(jarOrDir)) { + cp.addClazzpathUnit(jarOrDir) } + } - companion object { - @JvmStatic - fun forProject( - apiJars: FileCollection, - sourceSetsClassesDirs: Iterable, - toMinimize: FileCollection, - ): UnusedTracker { - return UnusedTracker(sourceSetsClassesDirs, apiJars, toMinimize) - } + companion object { + @JvmStatic + fun forProject( + apiJars: FileCollection, + sourceSetsClassesDirs: Iterable, + toMinimize: FileCollection, + ): UnusedTracker { + return UnusedTracker(sourceSetsClassesDirs, apiJars, toMinimize) + } - @JvmStatic - fun getApiJarsFromProject(project: Project): FileCollection { - val apiDependencies = project.configurations.findByName("api")?.dependencies - ?: return project.files() - val runtimeConfiguration = project.configurations.findByName("runtimeClasspath") - ?: project.configurations.getByName("runtime") - val apiJars = mutableListOf() - apiDependencies.forEach { dep -> - when (dep) { - is ProjectDependency -> { - apiJars.addAll(getApiJarsFromProject(dep.dependencyProject)) - addJar(runtimeConfiguration, dep, apiJars) - } - is SelfResolvingDependency -> { - apiJars.addAll(dep.resolve()) - } - else -> { - addJar(runtimeConfiguration, dep, apiJars) - apiJars.add(runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } as File) - } - } - } - return project.files(apiJars) + @JvmStatic + fun getApiJarsFromProject(project: Project): FileCollection { + val apiDependencies = project.configurations.findByName("api")?.dependencies + ?: return project.files() + val runtimeConfiguration = project.configurations.findByName("runtimeClasspath") + ?: project.configurations.getByName("runtime") + val apiJars = mutableListOf() + apiDependencies.forEach { dep -> + when (dep) { + is ProjectDependency -> { + apiJars.addAll(getApiJarsFromProject(dep.dependencyProject)) + addJar(runtimeConfiguration, dep, apiJars) + } + is SelfResolvingDependency -> { + apiJars.addAll(dep.resolve()) + } + else -> { + addJar(runtimeConfiguration, dep, apiJars) + apiJars.add(runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } as File) + } } + } + return project.files(apiJars) + } - private fun addJar(config: Configuration, dep: Dependency, result: MutableList) { - config.find { isProjectDependencyFile(it, dep) }?.let { result.add(it) } - } + private fun addJar(config: Configuration, dep: Dependency, result: MutableList) { + config.find { isProjectDependencyFile(it, dep) }?.let { result.add(it) } + } - private fun isProjectDependencyFile(file: File, dep: Dependency): Boolean { - val fileName = file.name - val dependencyName = dep.name - return fileName == "$dependencyName.jar" || - (fileName.startsWith("$dependencyName-") && fileName.endsWith(".jar")) - } + private fun isProjectDependencyFile(file: File, dep: Dependency): Boolean { + val fileName = file.name + val dependencyName = dep.name + return fileName == "$dependencyName.jar" || + (fileName.startsWith("$dependencyName-") && fileName.endsWith(".jar")) } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 38179d9d1..24b0abb5d 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -3,9 +3,9 @@ package com.github.jengelman.gradle.plugins.shadow.internal import java.io.InputStream internal fun Class<*>.requireResourceAsText(name: String): String { - return requireResourceAsStream(name).bufferedReader().readText() + return requireResourceAsStream(name).bufferedReader().readText() } private fun Class<*>.requireResourceAsStream(name: String): InputStream { - return getResourceAsStream(name) ?: error("Resource $name not found.") + return getResourceAsStream(name) ?: error("Resource $name not found.") } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt index 834ee364d..45441c642 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt @@ -9,5 +9,5 @@ import org.gradle.api.Project * This allows older build logic to keep on working as if that old plugin ID was applied. */ abstract class LegacyShadowPlugin : Plugin { - override fun apply(project: Project): Unit = Unit + override fun apply(project: Project): Unit = Unit } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt index 8d3b321e5..ec06e07ff 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt @@ -3,20 +3,20 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.ShadowStats data class RelocateClassContext( - val className: String, - val stats: ShadowStats, + val className: String, + val stats: ShadowStats, ) { - class Builder { - private var className = "" - private var stats = ShadowStats() + class Builder { + private var className = "" + private var stats = ShadowStats() - fun className(className: String): Builder = apply { this.className = className } - fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } - fun build(): RelocateClassContext = RelocateClassContext(className, stats) - } + fun className(className: String): Builder = apply { this.className = className } + fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + fun build(): RelocateClassContext = RelocateClassContext(className, stats) + } - companion object { - @JvmStatic - fun builder(): Builder = Builder() - } + companion object { + @JvmStatic + fun builder(): Builder = Builder() + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt index 87babb5d6..a9cf9cce9 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt @@ -3,20 +3,20 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.ShadowStats data class RelocatePathContext( - val path: String, - val stats: ShadowStats, + val path: String, + val stats: ShadowStats, ) { - class Builder { - private var path = "" - private var stats = ShadowStats() + class Builder { + private var path = "" + private var stats = ShadowStats() - fun path(path: String): Builder = apply { this.path = path } - fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } - fun build(): RelocatePathContext = RelocatePathContext(path, stats) - } + fun path(path: String): Builder = apply { this.path = path } + fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + fun build(): RelocatePathContext = RelocatePathContext(path, stats) + } - companion object { - @JvmStatic - fun builder(): Builder = Builder() - } + companion object { + @JvmStatic + fun builder(): Builder = Builder() + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt index d754ba7a1..03fea7f5d 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -7,17 +7,17 @@ package com.github.jengelman.gradle.plugins.shadow.relocation * @author John Engelman */ interface Relocator { - fun canRelocatePath(path: String): Boolean + fun canRelocatePath(path: String): Boolean - fun relocatePath(context: RelocatePathContext): String + fun relocatePath(context: RelocatePathContext): String - fun canRelocateClass(className: String): Boolean + fun canRelocateClass(className: String): Boolean - fun relocateClass(context: RelocateClassContext): String + fun relocateClass(context: RelocateClassContext): String - fun applyToSourceContent(sourceContent: String): String + fun applyToSourceContent(sourceContent: String): String - companion object { - val ROLE: String = Relocator::class.java.name - } + companion object { + val ROLE: String = Relocator::class.java.name + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 4e3185eff..98feac8f3 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -14,143 +14,143 @@ import org.gradle.api.tasks.Optional */ @CacheableRelocator open class SimpleRelocator @JvmOverloads constructor( - pattern: String?, - shadedPattern: String?, - includes: List?, - excludes: List?, - private val _isRawString: Boolean = false, + pattern: String?, + shadedPattern: String?, + includes: List?, + excludes: List?, + private val _isRawString: Boolean = false, ) : Relocator { - private val _pattern: String? - private val _pathPattern: String - private val _shadedPattern: String? - private val _shadedPathPattern: String - private val _includes = mutableSetOf() - private val _excludes = mutableSetOf() - - init { - if (_isRawString) { - _pathPattern = pattern.orEmpty() - _shadedPathPattern = shadedPattern.orEmpty() - _pattern = null // not used for raw string relocator - _shadedPattern = null // not used for raw string relocator - } else { - if (pattern == null) { - _pattern = "" - _pathPattern = "" - } else { - _pattern = pattern.replace('/', '.') - _pathPattern = pattern.replace('.', '/') - } - if (shadedPattern == null) { - _shadedPattern = "hidden.${_pattern}" - _shadedPathPattern = "hidden/$_pathPattern" - } else { - _shadedPattern = shadedPattern.replace('/', '.') - _shadedPathPattern = shadedPattern.replace('.', '/') - } - } - _includes += normalizePatterns(includes) - _excludes += normalizePatterns(excludes) + private val _pattern: String? + private val _pathPattern: String + private val _shadedPattern: String? + private val _shadedPathPattern: String + private val _includes = mutableSetOf() + private val _excludes = mutableSetOf() + + init { + if (_isRawString) { + _pathPattern = pattern.orEmpty() + _shadedPathPattern = shadedPattern.orEmpty() + _pattern = null // not used for raw string relocator + _shadedPattern = null // not used for raw string relocator + } else { + if (pattern == null) { + _pattern = "" + _pathPattern = "" + } else { + _pattern = pattern.replace('/', '.') + _pathPattern = pattern.replace('.', '/') + } + if (shadedPattern == null) { + _shadedPattern = "hidden.${_pattern}" + _shadedPathPattern = "hidden/$_pathPattern" + } else { + _shadedPattern = shadedPattern.replace('/', '.') + _shadedPathPattern = shadedPattern.replace('.', '/') + } } - - @get:Input - @get:Optional - open val pattern: String? get() = _pattern - - @get:Input - open val pathPattern: String get() = _pathPattern - - @get:Input - @get:Optional - open val shadedPattern: String? get() = _shadedPattern - - @get:Input - open val shadedPathPattern: String get() = _shadedPathPattern - - @get:Input - open val isRawString: Boolean get() = _isRawString - - @get:Input - open val includes: Set get() = _includes - - @get:Input - open val excludes: Set get() = _excludes - - open fun include(pattern: String): SimpleRelocator = apply { - _includes += normalizePatterns(listOf(pattern)) + _includes += normalizePatterns(includes) + _excludes += normalizePatterns(excludes) + } + + @get:Input + @get:Optional + open val pattern: String? get() = _pattern + + @get:Input + open val pathPattern: String get() = _pathPattern + + @get:Input + @get:Optional + open val shadedPattern: String? get() = _shadedPattern + + @get:Input + open val shadedPathPattern: String get() = _shadedPathPattern + + @get:Input + open val isRawString: Boolean get() = _isRawString + + @get:Input + open val includes: Set get() = _includes + + @get:Input + open val excludes: Set get() = _excludes + + open fun include(pattern: String): SimpleRelocator = apply { + _includes += normalizePatterns(listOf(pattern)) + } + + open fun exclude(pattern: String): SimpleRelocator = apply { + _excludes += normalizePatterns(listOf(pattern)) + } + + override fun canRelocatePath(path: String): Boolean { + if (_isRawString) return Pattern.compile(_pathPattern).matcher(path).find() + // If string is too short - no need to perform expensive string operations + if (path.length < _pathPattern.length) return false + val adjustedPath = if (path.endsWith(".class")) { + // Safeguard against strings containing only ".class" + if (path.length == 6) return false + path.dropLast(6) + } else { + path } - - open fun exclude(pattern: String): SimpleRelocator = apply { - _excludes += normalizePatterns(listOf(pattern)) + // Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties"). + val startIndex = if (adjustedPath.startsWith("/")) 1 else 0 + val pathStartsWithPattern = adjustedPath.startsWith(_pathPattern, startIndex) + return pathStartsWithPattern && isIncluded(adjustedPath) && !isExcluded(adjustedPath) + } + + override fun canRelocateClass(className: String): Boolean { + return !_isRawString && !className.contains('/') && canRelocatePath(className.replace('.', '/')) + } + + override fun relocatePath(context: RelocatePathContext): String { + val path = context.path + context.stats.relocate(_pathPattern, _shadedPathPattern) + return if (_isRawString) { + path.replace(_pathPattern.toRegex(), _shadedPathPattern) + } else { + path.replaceFirst(_pathPattern, _shadedPathPattern) } - - override fun canRelocatePath(path: String): Boolean { - if (_isRawString) return Pattern.compile(_pathPattern).matcher(path).find() - // If string is too short - no need to perform expensive string operations - if (path.length < _pathPattern.length) return false - val adjustedPath = if (path.endsWith(".class")) { - // Safeguard against strings containing only ".class" - if (path.length == 6) return false - path.dropLast(6) - } else { - path - } - // Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties"). - val startIndex = if (adjustedPath.startsWith("/")) 1 else 0 - val pathStartsWithPattern = adjustedPath.startsWith(_pathPattern, startIndex) - return pathStartsWithPattern && isIncluded(adjustedPath) && !isExcluded(adjustedPath) + } + + override fun relocateClass(context: RelocateClassContext): String { + context.stats.relocate(_pathPattern, _shadedPathPattern) + return context.className.replaceFirst(_pattern.orEmpty(), _shadedPattern.orEmpty()) + } + + override fun applyToSourceContent(sourceContent: String): String { + return if (_isRawString) { + sourceContent + } else { + sourceContent.replace("\\b$_pattern".toRegex(), _shadedPattern.orEmpty()) } - - override fun canRelocateClass(className: String): Boolean { - return !_isRawString && !className.contains('/') && canRelocatePath(className.replace('.', '/')) + } + + private fun normalizePatterns(patterns: Collection?) = buildSet { + for (pattern in patterns.orEmpty()) { + // Regex patterns don't need to be normalized and stay as is + if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { + add(pattern) + continue + } + + val classPattern = pattern.replace('.', '/') + add(classPattern) + + if (classPattern.endsWith("/*")) { + val packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) + add(packagePattern) + } } + } - override fun relocatePath(context: RelocatePathContext): String { - val path = context.path - context.stats.relocate(_pathPattern, _shadedPathPattern) - return if (_isRawString) { - path.replace(_pathPattern.toRegex(), _shadedPathPattern) - } else { - path.replaceFirst(_pathPattern, _shadedPathPattern) - } - } - - override fun relocateClass(context: RelocateClassContext): String { - context.stats.relocate(_pathPattern, _shadedPathPattern) - return context.className.replaceFirst(_pattern.orEmpty(), _shadedPattern.orEmpty()) - } - - override fun applyToSourceContent(sourceContent: String): String { - return if (_isRawString) { - sourceContent - } else { - sourceContent.replace("\\b$_pattern".toRegex(), _shadedPattern.orEmpty()) - } - } + private fun isIncluded(path: String): Boolean { + return _includes.isEmpty() || _includes.any { SelectorUtils.matchPath(it, path, "/", true) } + } - private fun normalizePatterns(patterns: Collection?) = buildSet { - for (pattern in patterns.orEmpty()) { - // Regex patterns don't need to be normalized and stay as is - if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { - add(pattern) - continue - } - - val classPattern = pattern.replace('.', '/') - add(classPattern) - - if (classPattern.endsWith("/*")) { - val packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) - add(packagePattern) - } - } - } - - private fun isIncluded(path: String): Boolean { - return _includes.isEmpty() || _includes.any { SelectorUtils.matchPath(it, path, "/", true) } - } - - private fun isExcluded(path: String): Boolean { - return _excludes.any { SelectorUtils.matchPath(it, path, "/", true) } - } + private fun isExcluded(path: String): Boolean { + return _excludes.any { SelectorUtils.matchPath(it, path, "/", true) } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt index 0a16a6037..20f9d7ff6 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt @@ -7,39 +7,39 @@ import org.gradle.api.java.archives.internal.DefaultManifest import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec open class DefaultInheritManifest @JvmOverloads constructor( - private val fileResolver: FileResolver, - private val internalManifest: DefaultManifest = DefaultManifest(fileResolver), + private val fileResolver: FileResolver, + private val internalManifest: DefaultManifest = DefaultManifest(fileResolver), ) : InheritManifest, - Manifest by internalManifest { - private val inheritMergeSpecs = mutableListOf() + Manifest by internalManifest { + private val inheritMergeSpecs = mutableListOf() - override fun inheritFrom( - vararg inheritPaths: Any, - ): InheritManifest { - return inheritFrom(inheritPaths = inheritPaths, action = null) - } + override fun inheritFrom( + vararg inheritPaths: Any, + ): InheritManifest { + return inheritFrom(inheritPaths = inheritPaths, action = null) + } - override fun inheritFrom( - vararg inheritPaths: Any, - action: Action<*>?, - ): InheritManifest = apply { - val mergeSpec = DefaultManifestMergeSpec() - mergeSpec.from(*inheritPaths) - inheritMergeSpecs.add(mergeSpec) - @Suppress("UNCHECKED_CAST") - (action as? Action)?.execute(mergeSpec) - } + override fun inheritFrom( + vararg inheritPaths: Any, + action: Action<*>?, + ): InheritManifest = apply { + val mergeSpec = DefaultManifestMergeSpec() + mergeSpec.from(*inheritPaths) + inheritMergeSpecs.add(mergeSpec) + @Suppress("UNCHECKED_CAST") + (action as? Action)?.execute(mergeSpec) + } - override fun getEffectiveManifest(): DefaultManifest { - var base = DefaultManifest(fileResolver) - inheritMergeSpecs.forEach { - base = it.merge(base, fileResolver) - } - base.from(internalManifest) - return base.effectiveManifest + override fun getEffectiveManifest(): DefaultManifest { + var base = DefaultManifest(fileResolver) + inheritMergeSpecs.forEach { + base = it.merge(base, fileResolver) } + base.from(internalManifest) + return base.effectiveManifest + } - override fun writeTo(path: Any): Manifest = apply { - effectiveManifest.writeTo(path) - } + override fun writeTo(path: Any): Manifest = apply { + effectiveManifest.writeTo(path) + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt index d14ecb18f..13c3a3ab3 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt @@ -4,7 +4,7 @@ import org.gradle.api.Action import org.gradle.api.java.archives.Manifest interface InheritManifest : Manifest { - fun inheritFrom(vararg inheritPaths: Any): InheritManifest + fun inheritFrom(vararg inheritPaths: Any): InheritManifest - fun inheritFrom(vararg inheritPaths: Any, action: Action<*>?): InheritManifest + fun inheritFrom(vararg inheritPaths: Any, action: Action<*>?): InheritManifest } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt index 35cc81b74..c7531ca6d 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt @@ -6,19 +6,19 @@ import org.gradle.api.tasks.TaskAction abstract class KnowsTask : DefaultTask() { - @TaskAction - fun knows() { - logger.info( - """ + @TaskAction + fun knows() { + logger.info( + """ No, The Shadow Knows.... ${this::class.java.requireResourceAsText("/shadowBanner.txt")} - """.trimIndent(), - ) - } + """.trimIndent(), + ) + } - companion object { - const val NAME: String = "knows" - const val DESC: String = "Do you know who knows?" - } + companion object { + const val NAME: String = "knows" + const val DESC: String = "Do you know who knows?" + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 244d3f53a..5149de444 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -39,409 +39,409 @@ import org.objectweb.asm.commons.ClassRemapper import org.slf4j.LoggerFactory open class ShadowCopyAction internal constructor( - private val zipFile: File, - private val compressor: ZipCompressor, - private val documentationRegistry: DocumentationRegistry, - private val encoding: String?, - private val transformers: List, - private val relocators: List, - private val patternSet: PatternSet, - private val stats: ShadowStats, - private val preserveFileTimestamps: Boolean, - private val minimizeJar: Boolean, - private val unusedTracker: UnusedTracker?, + private val zipFile: File, + private val compressor: ZipCompressor, + private val documentationRegistry: DocumentationRegistry, + private val encoding: String?, + private val transformers: List, + private val relocators: List, + private val patternSet: PatternSet, + private val stats: ShadowStats, + private val preserveFileTimestamps: Boolean, + private val minimizeJar: Boolean, + private val unusedTracker: UnusedTracker?, ) : CopyAction { - constructor( - zipFile: File, - compressor: ZipCompressor, - documentationRegistry: DocumentationRegistry, - encoding: String?, - transformers: List, - relocators: List, - patternSet: PatternSet, - stats: ShadowStats, - preserveFileTimestamps: Boolean, - minimizeJar: Boolean, - ) : this( - zipFile, - compressor, - documentationRegistry, - encoding, - transformers, - relocators, - patternSet, - stats, - preserveFileTimestamps, - minimizeJar, - null, - ) - - override fun execute(stream: CopyActionProcessingStream): WorkResult { - val unusedClasses = if (minimizeJar && unusedTracker != null) { - stream.process( - object : BaseStreamAction() { - override fun visitFile(fileDetails: FileCopyDetails) { - // All project sources are already present, we just need - // to deal with JAR dependencies. - if (isArchive(fileDetails)) { - unusedTracker.addDependency(fileDetails.file) - } - } - }, - ) - unusedTracker.findUnused() - } else { - emptySet() - } - - val zipOutStream = try { - compressor.createArchiveOutputStream(zipFile) as ZipOutputStream - } catch (e: Exception) { - throw GradleException("Could not create ZIP '$zipFile'", e) - } - - try { - zipOutStream.use { outputStream -> - stream.process( - StreamAction( - outputStream, - encoding, - transformers, - relocators, - patternSet, - unusedClasses, - stats, - ), - ) - processTransformers(outputStream) + constructor( + zipFile: File, + compressor: ZipCompressor, + documentationRegistry: DocumentationRegistry, + encoding: String?, + transformers: List, + relocators: List, + patternSet: PatternSet, + stats: ShadowStats, + preserveFileTimestamps: Boolean, + minimizeJar: Boolean, + ) : this( + zipFile, + compressor, + documentationRegistry, + encoding, + transformers, + relocators, + patternSet, + stats, + preserveFileTimestamps, + minimizeJar, + null, + ) + + override fun execute(stream: CopyActionProcessingStream): WorkResult { + val unusedClasses = if (minimizeJar && unusedTracker != null) { + stream.process( + object : BaseStreamAction() { + override fun visitFile(fileDetails: FileCopyDetails) { + // All project sources are already present, we just need + // to deal with JAR dependencies. + if (isArchive(fileDetails)) { + unusedTracker.addDependency(fileDetails.file) } - } catch (e: IOException) { - throw Zip64RequiredException( - "${e.cause?.message}\n\nTo build this archive, please enable the zip64 extension.\n" + - "See: ${documentationRegistry.getDslRefForProperty(Zip::class.java, "zip64")}", - ) - } - return WorkResults.didWork(true) + } + }, + ) + unusedTracker.findUnused() + } else { + emptySet() } - private fun processTransformers(stream: ZipOutputStream) { - transformers.forEach { transformer -> - if (transformer.hasTransformedResource()) { - transformer.modifyOutputStream(stream, preserveFileTimestamps) - } - } + val zipOutStream = try { + compressor.createArchiveOutputStream(zipFile) as ZipOutputStream + } catch (e: Exception) { + throw GradleException("Could not create ZIP '$zipFile'", e) } - private fun getArchiveTimeFor(timestamp: Long): Long { - return if (preserveFileTimestamps) timestamp else CONSTANT_TIME_FOR_ZIP_ENTRIES + try { + zipOutStream.use { outputStream -> + stream.process( + StreamAction( + outputStream, + encoding, + transformers, + relocators, + patternSet, + unusedClasses, + stats, + ), + ) + processTransformers(outputStream) + } + } catch (e: IOException) { + throw Zip64RequiredException( + "${e.cause?.message}\n\nTo build this archive, please enable the zip64 extension.\n" + + "See: ${documentationRegistry.getDslRefForProperty(Zip::class.java, "zip64")}", + ) } - - private fun setArchiveTimes(zipEntry: ZipEntry): ZipEntry { - if (!preserveFileTimestamps) { - zipEntry.time = CONSTANT_TIME_FOR_ZIP_ENTRIES - } - return zipEntry + return WorkResults.didWork(true) + } + + private fun processTransformers(stream: ZipOutputStream) { + transformers.forEach { transformer -> + if (transformer.hasTransformedResource()) { + transformer.modifyOutputStream(stream, preserveFileTimestamps) + } } + } - abstract class BaseStreamAction : CopyActionProcessingStreamAction { - protected fun isArchive(fileDetails: FileCopyDetails): Boolean { - return fileDetails.relativePath.pathString.endsWith(".jar") - } + private fun getArchiveTimeFor(timestamp: Long): Long { + return if (preserveFileTimestamps) timestamp else CONSTANT_TIME_FOR_ZIP_ENTRIES + } - protected fun isClass(fileDetails: FileCopyDetails): Boolean { - return fileDetails.path.endsWith(".class") - } + private fun setArchiveTimes(zipEntry: ZipEntry): ZipEntry { + if (!preserveFileTimestamps) { + zipEntry.time = CONSTANT_TIME_FOR_ZIP_ENTRIES + } + return zipEntry + } - override fun processFile(details: FileCopyDetailsInternal) { - if (details.isDirectory) visitDir(details) else visitFile(details) - } + abstract class BaseStreamAction : CopyActionProcessingStreamAction { + protected fun isArchive(fileDetails: FileCopyDetails): Boolean { + return fileDetails.relativePath.pathString.endsWith(".jar") + } - protected open fun visitDir(dirDetails: FileCopyDetails) {} + protected fun isClass(fileDetails: FileCopyDetails): Boolean { + return fileDetails.path.endsWith(".class") + } - protected abstract fun visitFile(fileDetails: FileCopyDetails) + override fun processFile(details: FileCopyDetailsInternal) { + if (details.isDirectory) visitDir(details) else visitFile(details) } - private inner class StreamAction( - private val zipOutStr: ZipOutputStream, - encoding: String?, - private val transformers: List, - private val relocators: List, - private val patternSet: PatternSet, - private val unused: Set, - private val stats: ShadowStats, - ) : BaseStreamAction() { - private val remapper = RelocatorRemapper(relocators, stats) - private val visitedFiles = mutableSetOf() - - init { - if (encoding != null) { - this.zipOutStr.setEncoding(encoding) - } - } + protected open fun visitDir(dirDetails: FileCopyDetails) {} - override fun visitFile(fileDetails: FileCopyDetails) { - if (!isArchive(fileDetails)) { - try { - val isClass = isClass(fileDetails) - if (!remapper.hasRelocators() || !isClass) { - if (!isTransformable(fileDetails)) { - val mappedPath = remapper.map(fileDetails.relativePath.pathString) - val archiveEntry = ZipEntry(mappedPath) - archiveEntry.time = getArchiveTimeFor(fileDetails.lastModified) - archiveEntry.unixMode = UnixStat.FILE_FLAG or fileDetails.permissions.toUnixNumeric() - zipOutStr.putNextEntry(archiveEntry) - fileDetails.copyTo(zipOutStr) - zipOutStr.closeEntry() - } else { - transform(fileDetails) - } - } else if (isClass && !isUnused(fileDetails.path)) { - remapClass(fileDetails) - } - recordVisit(fileDetails.relativePath) - } catch (e: Exception) { - throw GradleException("Could not add $fileDetails to ZIP '$zipFile'.", e) - } - } else { - processArchive(fileDetails) - } - } + protected abstract fun visitFile(fileDetails: FileCopyDetails) + } - private fun recordVisit(path: RelativePath): Boolean { - return visitedFiles.add(path.pathString) - } + private inner class StreamAction( + private val zipOutStr: ZipOutputStream, + encoding: String?, + private val transformers: List, + private val relocators: List, + private val patternSet: PatternSet, + private val unused: Set, + private val stats: ShadowStats, + ) : BaseStreamAction() { + private val remapper = RelocatorRemapper(relocators, stats) + private val visitedFiles = mutableSetOf() + + init { + if (encoding != null) { + this.zipOutStr.setEncoding(encoding) + } + } - private fun processArchive(fileDetails: FileCopyDetails) { - stats.startJar() - ZipFile(fileDetails.file).use { archive -> - archive.entries.asSequence() - .map { - ArchiveFileTreeElement(RelativeArchivePath(it)) - } - .filter { - patternSet.asSpec.isSatisfiedBy(it.asFileTreeElement()) - }.forEach { archiveElement -> - if (archiveElement.relativePath.isFile) { - visitArchiveFile(archiveElement, archive) - } - } + override fun visitFile(fileDetails: FileCopyDetails) { + if (!isArchive(fileDetails)) { + try { + val isClass = isClass(fileDetails) + if (!remapper.hasRelocators() || !isClass) { + if (!isTransformable(fileDetails)) { + val mappedPath = remapper.map(fileDetails.relativePath.pathString) + val archiveEntry = ZipEntry(mappedPath) + archiveEntry.time = getArchiveTimeFor(fileDetails.lastModified) + archiveEntry.unixMode = UnixStat.FILE_FLAG or fileDetails.permissions.toUnixNumeric() + zipOutStr.putNextEntry(archiveEntry) + fileDetails.copyTo(zipOutStr) + zipOutStr.closeEntry() + } else { + transform(fileDetails) } - stats.finishJar() + } else if (isClass && !isUnused(fileDetails.path)) { + remapClass(fileDetails) + } + recordVisit(fileDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $fileDetails to ZIP '$zipFile'.", e) } + } else { + processArchive(fileDetails) + } + } - private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { - if (recordVisit(archiveDir)) { - zipOutStr.putNextEntry(archiveDir.entry) - zipOutStr.closeEntry() - } - } + private fun recordVisit(path: RelativePath): Boolean { + return visitedFiles.add(path.pathString) + } - private fun visitArchiveFile(archiveFile: ArchiveFileTreeElement, archive: ZipFile) { - val archiveFilePath = archiveFile.relativePath - if (archiveFile.isClassFile || !isTransformable(archiveFile)) { - if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { - if (!remapper.hasRelocators() || !archiveFile.isClassFile) { - copyArchiveEntry(archiveFilePath, archive) - } else { - remapClass(archiveFilePath, archive) - } - } - } else { - transform(archiveFile, archive) + private fun processArchive(fileDetails: FileCopyDetails) { + stats.startJar() + ZipFile(fileDetails.file).use { archive -> + archive.entries.asSequence() + .map { + ArchiveFileTreeElement(RelativeArchivePath(it)) + } + .filter { + patternSet.asSpec.isSatisfiedBy(it.asFileTreeElement()) + }.forEach { archiveElement -> + if (archiveElement.relativePath.isFile) { + visitArchiveFile(archiveElement, archive) } - } + } + } + stats.finishJar() + } - private fun addParentDirectories(file: RelativeArchivePath?) { - file?.let { - addParentDirectories(it.parent) - if (!it.isFile) { - visitArchiveDirectory(it) - } - } - } + private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { + if (recordVisit(archiveDir)) { + zipOutStr.putNextEntry(archiveDir.entry) + zipOutStr.closeEntry() + } + } - private fun isUnused(classPath: String): Boolean { - val classPathWithoutExtension = classPath.substringBeforeLast(".") - val className = classPathWithoutExtension.replace('/', '.') - val result = unused.contains(className) - if (result) { - logger.debug("Dropping unused class: $className") - } - return result + private fun visitArchiveFile(archiveFile: ArchiveFileTreeElement, archive: ZipFile) { + val archiveFilePath = archiveFile.relativePath + if (archiveFile.isClassFile || !isTransformable(archiveFile)) { + if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { + if (!remapper.hasRelocators() || !archiveFile.isClassFile) { + copyArchiveEntry(archiveFilePath, archive) + } else { + remapClass(archiveFilePath, archive) + } } + } else { + transform(archiveFile, archive) + } + } - private fun remapClass(file: RelativeArchivePath, archive: ZipFile) { - if (file.isClassFile) { - val zipEntry = setArchiveTimes(ZipEntry(remapper.mapPath(file) + ".class")) - addParentDirectories(RelativeArchivePath(zipEntry)) - remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) - } + private fun addParentDirectories(file: RelativeArchivePath?) { + file?.let { + addParentDirectories(it.parent) + if (!it.isFile) { + visitArchiveDirectory(it) } + } + } - private fun remapClass(fileCopyDetails: FileCopyDetails) { - if (fileCopyDetails.name.endsWith(".class")) { - fileCopyDetails.file.inputStream().use { - remapClass(it, fileCopyDetails.path, fileCopyDetails.lastModified) - } - } - } + private fun isUnused(classPath: String): Boolean { + val classPathWithoutExtension = classPath.substringBeforeLast(".") + val className = classPathWithoutExtension.replace('/', '.') + val result = unused.contains(className) + if (result) { + logger.debug("Dropping unused class: $className") + } + return result + } - /** - * Applies remapping to the given class with the specified relocation path. The remapped class is then written - * to the zip file. [classInputStream] is closed automatically to prevent future file leaks. - * See #364 and #408. - */ - private fun remapClass(classInputStream: InputStream, path: String, lastModified: Long) { - // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. - // Copying the original constant pool should be avoided because it would keep references - // to the original class names. This is not a problem at runtime (because these entries in the - // constant pool are never used), but confuses some tools such as Felix' maven-bundle-plugin - // that use the constant pool to determine the dependencies of a class. - val cw = ClassWriter(0) - val cr = ClassReader(classInputStream) - val cv = ClassRemapper(cw, remapper) - - try { - cr.accept(cv, ClassReader.EXPAND_FRAMES) - } catch (t: Throwable) { - throw GradleException("Error in ASM processing class $path", t) - } + private fun remapClass(file: RelativeArchivePath, archive: ZipFile) { + if (file.isClassFile) { + val zipEntry = setArchiveTimes(ZipEntry(remapper.mapPath(file) + ".class")) + addParentDirectories(RelativeArchivePath(zipEntry)) + remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) + } + } - val renamedClass = cw.toByteArray() - // Temporarily remove the multi-release prefix. - val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() - val newPath = path.replace(multiReleasePrefix, "") - val mappedName = multiReleasePrefix + remapper.mapPath(newPath) - try { - // Now we put it back on so the class file is written out with the right extension. - val archiveEntry = ZipEntry("$mappedName.class") - archiveEntry.time = getArchiveTimeFor(lastModified) - zipOutStr.putNextEntry(archiveEntry) - renamedClass.inputStream().use { - it.copyTo(zipOutStr) - } - zipOutStr.closeEntry() - } catch (ignored: ZipException) { - logger.warn("We have a duplicate $mappedName in source project") - } + private fun remapClass(fileCopyDetails: FileCopyDetails) { + if (fileCopyDetails.name.endsWith(".class")) { + fileCopyDetails.file.inputStream().use { + remapClass(it, fileCopyDetails.path, fileCopyDetails.lastModified) } + } + } - private fun copyArchiveEntry(archiveFile: RelativeArchivePath, archive: ZipFile) { - val mappedPath = remapper.map(archiveFile.entry.name) - val entry = ZipEntry(mappedPath) - entry.time = getArchiveTimeFor(archiveFile.entry.time) - val mappedFile = RelativeArchivePath(entry) - addParentDirectories(mappedFile) - zipOutStr.putNextEntry(mappedFile.entry) - archive.getInputStream(archiveFile.entry).use { - it.copyTo(zipOutStr) - } - zipOutStr.closeEntry() + /** + * Applies remapping to the given class with the specified relocation path. The remapped class is then written + * to the zip file. [classInputStream] is closed automatically to prevent future file leaks. + * See #364 and #408. + */ + private fun remapClass(classInputStream: InputStream, path: String, lastModified: Long) { + // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. + // Copying the original constant pool should be avoided because it would keep references + // to the original class names. This is not a problem at runtime (because these entries in the + // constant pool are never used), but confuses some tools such as Felix' maven-bundle-plugin + // that use the constant pool to determine the dependencies of a class. + val cw = ClassWriter(0) + val cr = ClassReader(classInputStream) + val cv = ClassRemapper(cw, remapper) + + try { + cr.accept(cv, ClassReader.EXPAND_FRAMES) + } catch (t: Throwable) { + throw GradleException("Error in ASM processing class $path", t) + } + + val renamedClass = cw.toByteArray() + // Temporarily remove the multi-release prefix. + val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() + val newPath = path.replace(multiReleasePrefix, "") + val mappedName = multiReleasePrefix + remapper.mapPath(newPath) + try { + // Now we put it back on so the class file is written out with the right extension. + val archiveEntry = ZipEntry("$mappedName.class") + archiveEntry.time = getArchiveTimeFor(lastModified) + zipOutStr.putNextEntry(archiveEntry) + renamedClass.inputStream().use { + it.copyTo(zipOutStr) } + zipOutStr.closeEntry() + } catch (ignored: ZipException) { + logger.warn("We have a duplicate $mappedName in source project") + } + } - override fun visitDir(dirDetails: FileCopyDetails) { - try { - // Trailing slash in name indicates that entry is a directory - val path = dirDetails.relativePath.pathString + "/" - val archiveEntry = ZipEntry(path) - archiveEntry.time = getArchiveTimeFor(dirDetails.lastModified) - archiveEntry.unixMode = UnixStat.DIR_FLAG or dirDetails.permissions.toUnixNumeric() - zipOutStr.putNextEntry(archiveEntry) - zipOutStr.closeEntry() - recordVisit(dirDetails.relativePath) - } catch (e: Exception) { - throw GradleException("Could not add $dirDetails to ZIP '$zipFile'.", e) - } - } + private fun copyArchiveEntry(archiveFile: RelativeArchivePath, archive: ZipFile) { + val mappedPath = remapper.map(archiveFile.entry.name) + val entry = ZipEntry(mappedPath) + entry.time = getArchiveTimeFor(archiveFile.entry.time) + val mappedFile = RelativeArchivePath(entry) + addParentDirectories(mappedFile) + zipOutStr.putNextEntry(mappedFile.entry) + archive.getInputStream(archiveFile.entry).use { + it.copyTo(zipOutStr) + } + zipOutStr.closeEntry() + } - private fun transform(element: ArchiveFileTreeElement, archive: ZipFile) { - transformAndClose(element, archive.getInputStream(element.relativePath.entry)) - } + override fun visitDir(dirDetails: FileCopyDetails) { + try { + // Trailing slash in name indicates that entry is a directory + val path = dirDetails.relativePath.pathString + "/" + val archiveEntry = ZipEntry(path) + archiveEntry.time = getArchiveTimeFor(dirDetails.lastModified) + archiveEntry.unixMode = UnixStat.DIR_FLAG or dirDetails.permissions.toUnixNumeric() + zipOutStr.putNextEntry(archiveEntry) + zipOutStr.closeEntry() + recordVisit(dirDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $dirDetails to ZIP '$zipFile'.", e) + } + } - private fun transform(details: FileCopyDetails) { - transformAndClose(details, details.file.inputStream()) - } + private fun transform(element: ArchiveFileTreeElement, archive: ZipFile) { + transformAndClose(element, archive.getInputStream(element.relativePath.entry)) + } - private fun transformAndClose(element: FileTreeElement, inputStream: InputStream) { - inputStream.use { steam -> - val mappedPath = remapper.map(element.relativePath.pathString) - transformers.find { - it.canTransformResource(element) - }?.transform( - TransformerContext.builder() - .path(mappedPath) - .inputStream(steam) - .relocators(relocators) - .stats(stats) - .build(), - ) - } - } + private fun transform(details: FileCopyDetails) { + transformAndClose(details, details.file.inputStream()) + } - private fun isTransformable(element: FileTreeElement): Boolean { - return transformers.any { it.canTransformResource(element) } - } + private fun transformAndClose(element: FileTreeElement, inputStream: InputStream) { + inputStream.use { steam -> + val mappedPath = remapper.map(element.relativePath.pathString) + transformers.find { + it.canTransformResource(element) + }?.transform( + TransformerContext.builder() + .path(mappedPath) + .inputStream(steam) + .relocators(relocators) + .stats(stats) + .build(), + ) + } } - open inner class RelativeArchivePath( - open val entry: ZipEntry, - ) : RelativePath( - !entry.isDirectory, - // `dir/` will be split into ["dir", ""], we have to trim empty segments here. - *entry.name.split('/').filter(CharSequence::isNotEmpty).toTypedArray(), - ) { - open val isClassFile: Boolean get() = lastName.endsWith(".class") - - override fun getParent(): RelativeArchivePath? { - return if (segments.size <= 1) { - null - } else { - // Parent is always a directory so add / to the end of the path - val parentPath = segments.dropLast(1).joinToString("/") + "/" - val entry = setArchiveTimes(ZipEntry(parentPath)) - RelativeArchivePath(entry) - } - } + private fun isTransformable(element: FileTreeElement): Boolean { + return transformers.any { it.canTransformResource(element) } + } + } + + open inner class RelativeArchivePath( + open val entry: ZipEntry, + ) : RelativePath( + !entry.isDirectory, + // `dir/` will be split into ["dir", ""], we have to trim empty segments here. + *entry.name.split('/').filter(CharSequence::isNotEmpty).toTypedArray(), + ) { + open val isClassFile: Boolean get() = lastName.endsWith(".class") + + override fun getParent(): RelativeArchivePath? { + return if (segments.size <= 1) { + null + } else { + // Parent is always a directory so add / to the end of the path + val parentPath = segments.dropLast(1).joinToString("/") + "/" + val entry = setArchiveTimes(ZipEntry(parentPath)) + RelativeArchivePath(entry) + } } + } - open class ArchiveFileTreeElement( - private val archivePath: RelativeArchivePath, - ) : FileTreeElement { - open val isClassFile: Boolean get() = archivePath.isClassFile + open class ArchiveFileTreeElement( + private val archivePath: RelativeArchivePath, + ) : FileTreeElement { + open val isClassFile: Boolean get() = archivePath.isClassFile - override fun isDirectory(): Boolean = archivePath.entry.isDirectory + override fun isDirectory(): Boolean = archivePath.entry.isDirectory - override fun getLastModified(): Long = archivePath.entry.lastModifiedDate.time + override fun getLastModified(): Long = archivePath.entry.lastModifiedDate.time - override fun getSize(): Long = archivePath.entry.size + override fun getSize(): Long = archivePath.entry.size - override fun getName(): String = archivePath.pathString + override fun getName(): String = archivePath.pathString - override fun getPath(): String = archivePath.lastName + override fun getPath(): String = archivePath.lastName - override fun getRelativePath(): RelativeArchivePath = archivePath + override fun getRelativePath(): RelativeArchivePath = archivePath - @Deprecated("Deprecated in Java") - override fun getMode(): Int = archivePath.entry.unixMode + @Deprecated("Deprecated in Java") + override fun getMode(): Int = archivePath.entry.unixMode - override fun getPermissions(): FilePermissions = DefaultFilePermissions(mode) + override fun getPermissions(): FilePermissions = DefaultFilePermissions(mode) - open fun asFileTreeElement(): FileTreeElement { - return DefaultFileTreeElement(null, RelativePath(!isDirectory, *archivePath.segments), null, null) - } + open fun asFileTreeElement(): FileTreeElement { + return DefaultFileTreeElement(null, RelativePath(!isDirectory, *archivePath.segments), null, null) + } - override fun getFile(): File = throw UnsupportedOperationException() + override fun getFile(): File = throw UnsupportedOperationException() - override fun open(): InputStream = throw UnsupportedOperationException() + override fun open(): InputStream = throw UnsupportedOperationException() - override fun copyTo(outputStream: OutputStream): Unit = throw UnsupportedOperationException() + override fun copyTo(outputStream: OutputStream): Unit = throw UnsupportedOperationException() - override fun copyTo(file: File): Boolean = throw UnsupportedOperationException() - } + override fun copyTo(file: File): Boolean = throw UnsupportedOperationException() + } - companion object { - private val logger = LoggerFactory.getLogger(ShadowCopyAction::class.java) - val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis - } + companion object { + private val logger = LoggerFactory.getLogger(ShadowCopyAction::class.java) + val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 912a9bf01..bdb9806f0 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -42,303 +42,303 @@ import org.gradle.api.tasks.util.PatternSet @CacheableTask abstract class ShadowJar : - Jar(), - ShadowSpec { - private val _transformers = mutableListOf() - private val _relocators = mutableListOf() - private val _configurations = mutableListOf() - private val _stats = ShadowStats() - private val _includedDependencies = project.files(Callable { _dependencyFilter.resolve(_configurations) }) - - @Transient - private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) - - private var minimizeJar = false - private var _isEnableRelocation = false - private var _relocationPrefix = ShadowBasePlugin.SHADOW - private var _toMinimize: FileCollection? = null - private var _apiJars: FileCollection? = null - private var _sourceSetsClassesDirs: FileCollection? = null - - @Transient - private var _dependencyFilter: DependencyFilter = DefaultDependencyFilter(project) - - init { - duplicatesStrategy = DuplicatesStrategy.INCLUDE - manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) - - inputs.property("minimize") { minimizeJar } - outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { - _transformers.any { !isCacheableTransform(it::class.java) } || - _relocators.any { !isCacheableRelocator(it::class.java) } - } + Jar(), + ShadowSpec { + private val _transformers = mutableListOf() + private val _relocators = mutableListOf() + private val _configurations = mutableListOf() + private val _stats = ShadowStats() + private val _includedDependencies = project.files(Callable { _dependencyFilter.resolve(_configurations) }) + + @Transient + private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) + + private var minimizeJar = false + private var _isEnableRelocation = false + private var _relocationPrefix = ShadowBasePlugin.SHADOW + private var _toMinimize: FileCollection? = null + private var _apiJars: FileCollection? = null + private var _sourceSetsClassesDirs: FileCollection? = null + + @Transient + private var _dependencyFilter: DependencyFilter = DefaultDependencyFilter(project) + + init { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) + + inputs.property("minimize") { minimizeJar } + outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { + _transformers.any { !isCacheableTransform(it::class.java) } || + _relocators.any { !isCacheableRelocator(it::class.java) } } - - @get:Internal - override val stats: ShadowStats get() = _stats - - @get:Classpath - val toMinimize: FileCollection - get() { - if (_toMinimize == null) { - _toMinimize = if (minimizeJar) { - dependencyFilterForMinimize.resolve(_configurations) - .minus(apiJars) - } else { - project.objects.fileCollection() - } - } - return _toMinimize!! - } - - @get:Classpath - val apiJars: FileCollection - get() { - if (_apiJars == null) { - _apiJars = if (minimizeJar) { - UnusedTracker.getApiJarsFromProject(project) - } else { - project.objects.fileCollection() - } - } - return _apiJars!! - } - - @get:InputFiles - @get:PathSensitive(PathSensitivity.RELATIVE) - val sourceSetsClassesDirs: FileCollection - get() { - if (_sourceSetsClassesDirs == null) { - val allClassesDirs = project.objects.fileCollection() - if (minimizeJar) { - project.extensions.getByType(SourceSetContainer::class.java).forEach { sourceSet -> - allClassesDirs.from(sourceSet.output.classesDirs) - } - } - _sourceSetsClassesDirs = allClassesDirs.filter { it.isDirectory } - } - return _sourceSetsClassesDirs!! - } - - @get:Classpath - val includedDependencies: FileCollection get() = _includedDependencies - - @get:Internal - val rootPatternSet: PatternSet - get() { - return (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet - } - - @get:Internal - val internalCompressor: ZipCompressor - get() { - return when (entryCompression) { - ZipEntryCompression.DEFLATED -> DefaultZipCompressor(isZip64, ZipOutputStream.DEFLATED) - ZipEntryCompression.STORED -> DefaultZipCompressor(isZip64, ZipOutputStream.STORED) - else -> throw IllegalArgumentException("Unknown Compression type $entryCompression") - } - } - - @get:Nested - var transformers: List - get() = _transformers - set(value) { - _transformers.clear() - _transformers.addAll(value) - } - - @get:Nested - var relocators: List - get() = _relocators - set(value) { - _relocators.clear() - _relocators.addAll(value) - } - - @get:Classpath - @get:Optional - var configurations: List - get() = _configurations - set(value) { - _configurations.clear() - _configurations.addAll(value) - } - - @get:Internal - var dependencyFilter: DependencyFilter - get() = _dependencyFilter - set(value) { - _dependencyFilter = value - } - - @get:Input - var isEnableRelocation: Boolean - get() = _isEnableRelocation - set(value) { - _isEnableRelocation = value - } - - @get:Input - var relocationPrefix: String - get() = _relocationPrefix - set(value) { - _relocationPrefix = value + } + + @get:Internal + override val stats: ShadowStats get() = _stats + + @get:Classpath + val toMinimize: FileCollection + get() { + if (_toMinimize == null) { + _toMinimize = if (minimizeJar) { + dependencyFilterForMinimize.resolve(_configurations) + .minus(apiJars) + } else { + project.objects.fileCollection() } - - @Internal - override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest - - override fun minimize(): ShadowJar = apply { - minimizeJar = true + } + return _toMinimize!! } - override fun minimize(action: Action?): ShadowJar = apply { - minimize() - action?.execute(dependencyFilterForMinimize) - } - - override fun createCopyAction(): CopyAction { - val documentationRegistry = services.get(DocumentationRegistry::class.java) - val unusedTracker = if (minimizeJar) { - UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) + @get:Classpath + val apiJars: FileCollection + get() { + if (_apiJars == null) { + _apiJars = if (minimizeJar) { + UnusedTracker.getApiJarsFromProject(project) } else { - null + project.objects.fileCollection() } - return ShadowCopyAction( - archiveFile.get().asFile, - internalCompressor, - documentationRegistry, - metadataCharset, - _transformers, - _relocators, - rootPatternSet, - _stats, - isPreserveFileTimestamps, - minimizeJar, - unusedTracker, - ) - } - - override fun dependencies(action: Action?): ShadowJar = apply { - action?.execute(_dependencyFilter) - } - - override fun transform(clazz: Class): ShadowJar { - return transform(clazz, null) - } - - override fun transform(clazz: Class, action: Action?): ShadowJar = apply { - val transformer = clazz.getDeclaredConstructor().newInstance() - addTransform(transformer, action) - } - - override fun transform(transformer: Transformer): ShadowJar = apply { - addTransform(transformer, null) + } + return _apiJars!! } - override fun mergeServiceFiles(): ShadowJar { - return runCatching { - transform(ServiceFileTransformer::class.java, null) - }.getOrDefault(this) + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + val sourceSetsClassesDirs: FileCollection + get() { + if (_sourceSetsClassesDirs == null) { + val allClassesDirs = project.objects.fileCollection() + if (minimizeJar) { + project.extensions.getByType(SourceSetContainer::class.java).forEach { sourceSet -> + allClassesDirs.from(sourceSet.output.classesDirs) + } + } + _sourceSetsClassesDirs = allClassesDirs.filter { it.isDirectory } + } + return _sourceSetsClassesDirs!! } - override fun mergeServiceFiles(rootPath: String): ShadowJar { - return runCatching { - transform(ServiceFileTransformer::class.java) { - it.setPath(rootPath) - } - }.getOrDefault(this) - } + @get:Classpath + val includedDependencies: FileCollection get() = _includedDependencies - override fun mergeServiceFiles(action: Action?): ShadowJar { - return runCatching { - transform(ServiceFileTransformer::class.java, action) - }.getOrDefault(this) + @get:Internal + val rootPatternSet: PatternSet + get() { + return (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet } - override fun mergeGroovyExtensionModules(): ShadowJar { - return runCatching { - transform(GroovyExtensionModuleTransformer::class.java, null) - }.getOrDefault(this) + @get:Internal + val internalCompressor: ZipCompressor + get() { + return when (entryCompression) { + ZipEntryCompression.DEFLATED -> DefaultZipCompressor(isZip64, ZipOutputStream.DEFLATED) + ZipEntryCompression.STORED -> DefaultZipCompressor(isZip64, ZipOutputStream.STORED) + else -> throw IllegalArgumentException("Unknown Compression type $entryCompression") + } } - override fun append(resourcePath: String): ShadowJar { - return runCatching { - transform(AppendingTransformer::class.java) { - it.resource = resourcePath - } - }.getOrDefault(this) + @get:Nested + var transformers: List + get() = _transformers + set(value) { + _transformers.clear() + _transformers.addAll(value) } - override fun relocate(pattern: String, destination: String): ShadowJar { - return relocate(pattern, destination, null) + @get:Nested + var relocators: List + get() = _relocators + set(value) { + _relocators.clear() + _relocators.addAll(value) } - override fun relocate( - pattern: String, - destination: String, - action: Action?, - ): ShadowJar = apply { - val relocator = SimpleRelocator(pattern, destination, mutableListOf(), mutableListOf()) - addRelocator(relocator, action) + @get:Classpath + @get:Optional + var configurations: List + get() = _configurations + set(value) { + _configurations.clear() + _configurations.addAll(value) } - override fun relocate(relocator: Relocator): ShadowJar = apply { - addRelocator(relocator, null) + @get:Internal + var dependencyFilter: DependencyFilter + get() = _dependencyFilter + set(value) { + _dependencyFilter = value } - override fun relocate(clazz: Class): ShadowJar { - return relocate(clazz, null) + @get:Input + var isEnableRelocation: Boolean + get() = _isEnableRelocation + set(value) { + _isEnableRelocation = value } - override fun relocate(clazz: Class, action: Action?): ShadowJar = apply { - val relocator = clazz.getDeclaredConstructor().newInstance() - addRelocator(relocator, action) + @get:Input + var relocationPrefix: String + get() = _relocationPrefix + set(value) { + _relocationPrefix = value } - @TaskAction - override fun copy() { - if (_isEnableRelocation) { - configureRelocation() - } - from(_includedDependencies) - super.copy() - logger.info(_stats.toString()) - } + @Internal + override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest - private fun addRelocator(relocator: R, configure: Action?) { - configure?.execute(relocator) - _relocators.add(relocator) - } + override fun minimize(): ShadowJar = apply { + minimizeJar = true + } - private fun addTransform(transformer: T, action: Action?) { - action?.execute(transformer) - _transformers.add(transformer) - } + override fun minimize(action: Action?): ShadowJar = apply { + minimize() + action?.execute(dependencyFilterForMinimize) + } - private fun isCacheableRelocator(clazz: Class): Boolean { - return clazz.isAnnotationPresent(CacheableRelocator::class.java) + override fun createCopyAction(): CopyAction { + val documentationRegistry = services.get(DocumentationRegistry::class.java) + val unusedTracker = if (minimizeJar) { + UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) + } else { + null } - - private fun isCacheableTransform(clazz: Class): Boolean { - return clazz.isAnnotationPresent(CacheableTransformer::class.java) + return ShadowCopyAction( + archiveFile.get().asFile, + internalCompressor, + documentationRegistry, + metadataCharset, + _transformers, + _relocators, + rootPatternSet, + _stats, + isPreserveFileTimestamps, + minimizeJar, + unusedTracker, + ) + } + + override fun dependencies(action: Action?): ShadowJar = apply { + action?.execute(_dependencyFilter) + } + + override fun transform(clazz: Class): ShadowJar { + return transform(clazz, null) + } + + override fun transform(clazz: Class, action: Action?): ShadowJar = apply { + val transformer = clazz.getDeclaredConstructor().newInstance() + addTransform(transformer, action) + } + + override fun transform(transformer: Transformer): ShadowJar = apply { + addTransform(transformer, null) + } + + override fun mergeServiceFiles(): ShadowJar { + return runCatching { + transform(ServiceFileTransformer::class.java, null) + }.getOrDefault(this) + } + + override fun mergeServiceFiles(rootPath: String): ShadowJar { + return runCatching { + transform(ServiceFileTransformer::class.java) { + it.setPath(rootPath) + } + }.getOrDefault(this) + } + + override fun mergeServiceFiles(action: Action?): ShadowJar { + return runCatching { + transform(ServiceFileTransformer::class.java, action) + }.getOrDefault(this) + } + + override fun mergeGroovyExtensionModules(): ShadowJar { + return runCatching { + transform(GroovyExtensionModuleTransformer::class.java, null) + }.getOrDefault(this) + } + + override fun append(resourcePath: String): ShadowJar { + return runCatching { + transform(AppendingTransformer::class.java) { + it.resource = resourcePath + } + }.getOrDefault(this) + } + + override fun relocate(pattern: String, destination: String): ShadowJar { + return relocate(pattern, destination, null) + } + + override fun relocate( + pattern: String, + destination: String, + action: Action?, + ): ShadowJar = apply { + val relocator = SimpleRelocator(pattern, destination, mutableListOf(), mutableListOf()) + addRelocator(relocator, action) + } + + override fun relocate(relocator: Relocator): ShadowJar = apply { + addRelocator(relocator, null) + } + + override fun relocate(clazz: Class): ShadowJar { + return relocate(clazz, null) + } + + override fun relocate(clazz: Class, action: Action?): ShadowJar = apply { + val relocator = clazz.getDeclaredConstructor().newInstance() + addRelocator(relocator, action) + } + + @TaskAction + override fun copy() { + if (_isEnableRelocation) { + configureRelocation() } - - private fun configureRelocation() { - val packages = mutableSetOf() - configurations.forEach { configuration -> - configuration.files.forEach { jarFile -> - JarFile(jarFile).use { jf -> - jf.entries().asSequence().forEach { entry -> - if (entry.name.endsWith(".class") && entry.name != "module-info.class") { - packages.add(entry.name.substringBeforeLast('/').replace('/', '.')) - } - } - } + from(_includedDependencies) + super.copy() + logger.info(_stats.toString()) + } + + private fun addRelocator(relocator: R, configure: Action?) { + configure?.execute(relocator) + _relocators.add(relocator) + } + + private fun addTransform(transformer: T, action: Action?) { + action?.execute(transformer) + _transformers.add(transformer) + } + + private fun isCacheableRelocator(clazz: Class): Boolean { + return clazz.isAnnotationPresent(CacheableRelocator::class.java) + } + + private fun isCacheableTransform(clazz: Class): Boolean { + return clazz.isAnnotationPresent(CacheableTransformer::class.java) + } + + private fun configureRelocation() { + val packages = mutableSetOf() + configurations.forEach { configuration -> + configuration.files.forEach { jarFile -> + JarFile(jarFile).use { jf -> + jf.entries().asSequence().forEach { entry -> + if (entry.name.endsWith(".class") && entry.name != "module-info.class") { + packages.add(entry.name.substringBeforeLast('/').replace('/', '.')) } + } } - packages.forEach { - relocate(it, "$_relocationPrefix.$it") - } + } + } + packages.forEach { + relocate(it, "$_relocationPrefix.$it") } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt index 04caf7315..826d201ba 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -11,61 +11,61 @@ import org.gradle.api.Action import org.gradle.api.file.CopySpec interface ShadowSpec : CopySpec { - fun minimize(): ShadowSpec + fun minimize(): ShadowSpec - fun minimize(action: Action?): ShadowSpec + fun minimize(action: Action?): ShadowSpec - fun dependencies(action: Action?): ShadowSpec + fun dependencies(action: Action?): ShadowSpec - @Throws( - InstantiationException::class, - IllegalAccessException::class, - NoSuchMethodException::class, - InvocationTargetException::class, - ) - fun transform(clazz: Class): ShadowSpec + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun transform(clazz: Class): ShadowSpec - @Throws( - InstantiationException::class, - IllegalAccessException::class, - NoSuchMethodException::class, - InvocationTargetException::class, - ) - fun transform(clazz: Class, action: Action?): ShadowSpec + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun transform(clazz: Class, action: Action?): ShadowSpec - fun transform(transformer: Transformer): ShadowSpec + fun transform(transformer: Transformer): ShadowSpec - fun mergeServiceFiles(): ShadowSpec + fun mergeServiceFiles(): ShadowSpec - fun mergeServiceFiles(rootPath: String): ShadowSpec + fun mergeServiceFiles(rootPath: String): ShadowSpec - fun mergeServiceFiles(action: Action?): ShadowSpec + fun mergeServiceFiles(action: Action?): ShadowSpec - fun mergeGroovyExtensionModules(): ShadowSpec + fun mergeGroovyExtensionModules(): ShadowSpec - fun append(resourcePath: String): ShadowSpec + fun append(resourcePath: String): ShadowSpec - fun relocate(pattern: String, destination: String): ShadowSpec + fun relocate(pattern: String, destination: String): ShadowSpec - fun relocate(pattern: String, destination: String, action: Action?): ShadowSpec + fun relocate(pattern: String, destination: String, action: Action?): ShadowSpec - fun relocate(relocator: Relocator): ShadowSpec + fun relocate(relocator: Relocator): ShadowSpec - @Throws( - InstantiationException::class, - IllegalAccessException::class, - NoSuchMethodException::class, - InvocationTargetException::class, - ) - fun relocate(clazz: Class): ShadowSpec + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun relocate(clazz: Class): ShadowSpec - @Throws( - InstantiationException::class, - IllegalAccessException::class, - NoSuchMethodException::class, - InvocationTargetException::class, - ) - fun relocate(clazz: Class, action: Action?): ShadowSpec + @Throws( + InstantiationException::class, + IllegalAccessException::class, + NoSuchMethodException::class, + InvocationTargetException::class, + ) + fun relocate(clazz: Class, action: Action?): ShadowSpec - val stats: ShadowStats + val stats: ShadowStats } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index 3bd32275a..132c9efa3 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -10,14 +10,14 @@ import org.gradle.api.file.FileTreeElement * @author John Engelman */ open class ApacheLicenseResourceTransformer : Transformer by NoOpTransformer { - override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString - return LICENSE_PATH.equals(path, ignoreCase = true) || - LICENSE_TXT_PATH.regionMatches(0, path, 0, LICENSE_TXT_PATH.length, ignoreCase = true) - } + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return LICENSE_PATH.equals(path, ignoreCase = true) || + LICENSE_TXT_PATH.regionMatches(0, path, 0, LICENSE_TXT_PATH.length, ignoreCase = true) + } - private companion object { - private const val LICENSE_PATH = "META-INF/LICENSE" - private const val LICENSE_TXT_PATH = "META-INF/LICENSE.txt" - } + private companion object { + private const val LICENSE_PATH = "META-INF/LICENSE" + private const val LICENSE_TXT_PATH = "META-INF/LICENSE.txt" + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 2fa195e77..5f1c3347f 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -19,160 +19,160 @@ import org.gradle.api.tasks.Optional * @author John Engelman */ open class ApacheNoticeResourceTransformer : Transformer { - private val entries = mutableSetOf() - private val organizationEntries = mutableMapOf>() - private val charset get() = if (encoding.isNullOrEmpty()) Charsets.UTF_8 else Charset.forName(encoding) + private val entries = mutableSetOf() + private val organizationEntries = mutableMapOf>() + private val charset get() = if (encoding.isNullOrEmpty()) Charsets.UTF_8 else Charset.forName(encoding) - /** - * MSHADE-101 :: NullPointerException when projectName is missing - */ - @get:Input - var projectName: String = "" + /** + * MSHADE-101 :: NullPointerException when projectName is missing + */ + @get:Input + var projectName: String = "" - @get:Input - var addHeader: Boolean = true + @get:Input + var addHeader: Boolean = true - @get:Input - var preamble1: String = """ + @get:Input + var preamble1: String = """ // ------------------------------------------------------------------ // NOTICE file corresponding to the section 4d of The Apache License, // Version 2.0, in this case for - """.trimIndent() - - @get:Input - var preamble2: String = "\n// ------------------------------------------------------------------\n" - - @get:Input - var preamble3: String = "This product includes software developed at\n" - - @get:Input - var organizationName: String = "The Apache Software Foundation" - - @get:Input - var organizationURL: String = "http://www.apache.org/" - - @get:Input - var inceptionYear: String = "2006" - - @get:Optional - @get:Input - var copyright: String? = null - - /** - * The file encoding of the `NOTICE` file. - */ - @get:Optional - @get:Input - var encoding: String? = null - - override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString - return NOTICE_PATH.equals(path, ignoreCase = true) || NOTICE_TXT_PATH.equals(path, ignoreCase = true) + """.trimIndent() + + @get:Input + var preamble2: String = "\n// ------------------------------------------------------------------\n" + + @get:Input + var preamble3: String = "This product includes software developed at\n" + + @get:Input + var organizationName: String = "The Apache Software Foundation" + + @get:Input + var organizationURL: String = "http://www.apache.org/" + + @get:Input + var inceptionYear: String = "2006" + + @get:Optional + @get:Input + var copyright: String? = null + + /** + * The file encoding of the `NOTICE` file. + */ + @get:Optional + @get:Input + var encoding: String? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return NOTICE_PATH.equals(path, ignoreCase = true) || NOTICE_TXT_PATH.equals(path, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + if (entries.isEmpty()) { + val year = SimpleDateFormat("yyyy").format(Date()).let { + if (inceptionYear != it) "$inceptionYear-$it" else it + } + // add headers + if (addHeader) { + entries.add("$preamble1$projectName$preamble2") + } else { + entries.add("") + } + // fake second entry, we'll look for a real one later + entries.add("$projectName\nCopyright $year $organizationName\n") + entries.add("$preamble3$organizationName ($organizationURL).\n") } - override fun transform(context: TransformerContext) { - if (entries.isEmpty()) { - val year = SimpleDateFormat("yyyy").format(Date()).let { - if (inceptionYear != it) "$inceptionYear-$it" else it - } - // add headers - if (addHeader) { - entries.add("$preamble1$projectName$preamble2") - } else { - entries.add("") + val reader = context.inputStream.bufferedReader(charset) + var line = reader.readLine() + val sb = StringBuffer() + var currentOrg: MutableSet? = null + var lineCount = 0 + while (line != null) { + val trimmedLine = line.trim() + if (!trimmedLine.startsWith("//")) { + if (trimmedLine.isNotEmpty()) { + if (trimmedLine.startsWith("- ")) { + // resource-bundle 1.3 mode + if (lineCount == 1 && sb.toString().contains("This product includes/uses software(s) developed by")) { + currentOrg = organizationEntries.getOrPut(sb.toString().trim()) { TreeSet() } + sb.setLength(0) + } else if (sb.isNotEmpty() && currentOrg != null) { + currentOrg.add(sb.toString()) + sb.setLength(0) } - // fake second entry, we'll look for a real one later - entries.add("$projectName\nCopyright $year $organizationName\n") - entries.add("$preamble3$organizationName ($organizationURL).\n") + } + sb.append(line).append("\n") + lineCount++ + } else { + val entry = sb.toString() + if (entry.startsWith(projectName) && entry.contains("Copyright ")) { + copyright = entry + } + if (currentOrg == null) { + entries.add(entry) + } else { + currentOrg.add(entry) + } + sb.setLength(0) + lineCount = 0 + currentOrg = null } + } - val reader = context.inputStream.bufferedReader(charset) - var line = reader.readLine() - val sb = StringBuffer() - var currentOrg: MutableSet? = null - var lineCount = 0 - while (line != null) { - val trimmedLine = line.trim() - if (!trimmedLine.startsWith("//")) { - if (trimmedLine.isNotEmpty()) { - if (trimmedLine.startsWith("- ")) { - // resource-bundle 1.3 mode - if (lineCount == 1 && sb.toString().contains("This product includes/uses software(s) developed by")) { - currentOrg = organizationEntries.getOrPut(sb.toString().trim()) { TreeSet() } - sb.setLength(0) - } else if (sb.isNotEmpty() && currentOrg != null) { - currentOrg.add(sb.toString()) - sb.setLength(0) - } - } - sb.append(line).append("\n") - lineCount++ - } else { - val entry = sb.toString() - if (entry.startsWith(projectName) && entry.contains("Copyright ")) { - copyright = entry - } - if (currentOrg == null) { - entries.add(entry) - } else { - currentOrg.add(entry) - } - sb.setLength(0) - lineCount = 0 - currentOrg = null - } - } - - line = reader.readLine() - } - if (sb.isNotEmpty()) { - if (currentOrg == null) { - entries.add(sb.toString()) - } else { - currentOrg.add(sb.toString()) - } - } + line = reader.readLine() } - - override fun hasTransformedResource(): Boolean = true - - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val zipEntry = ZipEntry(NOTICE_PATH) - zipEntry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, zipEntry.time) - os.putNextEntry(zipEntry) - - val writer = PrintWriter(os.writer(charset)) - - var count = 0 - for (line in entries) { - count++ - if (line == copyright && count != 2) continue - if (count == 2 && copyright != null) { - writer.print(copyright) - writer.print('\n') - } else { - writer.print(line) - writer.print('\n') - } - if (count == 3) { - // do org stuff - for ((key, value) in organizationEntries) { - writer.print(key) - writer.print('\n') - for (l in value) { - writer.print(l) - } - writer.print('\n') - } - } + if (sb.isNotEmpty()) { + if (currentOrg == null) { + entries.add(sb.toString()) + } else { + currentOrg.add(sb.toString()) + } + } + } + + override fun hasTransformedResource(): Boolean = true + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val zipEntry = ZipEntry(NOTICE_PATH) + zipEntry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, zipEntry.time) + os.putNextEntry(zipEntry) + + val writer = PrintWriter(os.writer(charset)) + + var count = 0 + for (line in entries) { + count++ + if (line == copyright && count != 2) continue + if (count == 2 && copyright != null) { + writer.print(copyright) + writer.print('\n') + } else { + writer.print(line) + writer.print('\n') + } + if (count == 3) { + // do org stuff + for ((key, value) in organizationEntries) { + writer.print(key) + writer.print('\n') + for (l in value) { + writer.print(l) + } + writer.print('\n') } - - writer.flush() - entries.clear() + } } - private companion object { - private const val NOTICE_PATH = "META-INF/NOTICE" - private const val NOTICE_TXT_PATH = "META-INF/NOTICE.txt" - } + writer.flush() + entries.clear() + } + + private companion object { + private const val NOTICE_PATH = "META-INF/NOTICE" + private const val NOTICE_TXT_PATH = "META-INF/NOTICE.txt" + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index f28b8014c..3cc27f60a 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -18,37 +18,37 @@ import org.gradle.api.tasks.Optional @CacheableTransformer @Suppress("ktlint:standard:backing-property-naming") open class AppendingTransformer : Transformer { - /** - * Defer initialization, see [issue 763](https://github.com/GradleUp/shadow/issues/763). - */ - private var _data: ByteArrayOutputStream? = null - private inline val data get() = if (_data == null) ByteArrayOutputStream().also { _data = it } else _data!! - - @get:Optional - @get:Input - var resource: String? = null - - override fun canTransformResource(element: FileTreeElement): Boolean { - return resource.equals(element.relativePath.pathString, ignoreCase = true) + /** + * Defer initialization, see [issue 763](https://github.com/GradleUp/shadow/issues/763). + */ + private var _data: ByteArrayOutputStream? = null + private inline val data get() = if (_data == null) ByteArrayOutputStream().also { _data = it } else _data!! + + @get:Optional + @get:Input + var resource: String? = null + + override fun canTransformResource(element: FileTreeElement): Boolean { + return resource.equals(element.relativePath.pathString, ignoreCase = true) + } + + override fun transform(context: TransformerContext) { + context.inputStream.use { + it.copyTo(data) + data.write('\n'.code) } + } - override fun transform(context: TransformerContext) { - context.inputStream.use { - it.copyTo(data) - data.write('\n'.code) - } - } - - override fun hasTransformedResource(): Boolean { - return data.size() > 0 - } + override fun hasTransformedResource(): Boolean { + return data.size() > 0 + } - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(requireNotNull(resource)) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(requireNotNull(resource)) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) - data.toByteArray().inputStream().copyTo(os) - data.reset() - } + data.toByteArray().inputStream().copyTo(os) + data.reset() + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index 258623830..8616a2605 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -22,113 +22,113 @@ import org.gradle.api.file.FileTreeElement * @author John Engelman */ open class ComponentsXmlResourceTransformer : Transformer { - private val components = mutableMapOf() - - override fun canTransformResource(element: FileTreeElement): Boolean { - return COMPONENTS_XML_PATH == element.relativePath.pathString - } - - override fun transform(context: TransformerContext) { - val newDom = try { - val bis = object : BufferedInputStream(context.inputStream) { - @Throws(IOException::class) - override fun close() { - // leave ZIP open - } - } - Xpp3DomBuilder.build(XmlStreamReader(bis)) - } catch (e: Exception) { - throw IOException("Error parsing components.xml in ${context.inputStream}", e) + private val components = mutableMapOf() + + override fun canTransformResource(element: FileTreeElement): Boolean { + return COMPONENTS_XML_PATH == element.relativePath.pathString + } + + override fun transform(context: TransformerContext) { + val newDom = try { + val bis = object : BufferedInputStream(context.inputStream) { + @Throws(IOException::class) + override fun close() { + // leave ZIP open } + } + Xpp3DomBuilder.build(XmlStreamReader(bis)) + } catch (e: Exception) { + throw IOException("Error parsing components.xml in ${context.inputStream}", e) + } - // Only try to merge in components if there are some elements in the component-set - if (newDom.getChild("components") == null) return - - val children = newDom.getChild("components").getChildren("component") - for (component in children) { - var role: String? = getValue(component, "role") - role = getRelocatedClass(role, context) - setValue(component, "role", role) - - val roleHint = getValue(component, "role-hint") - - var impl: String? = getValue(component, "implementation") - impl = getRelocatedClass(impl, context) - setValue(component, "implementation", impl) - - val key = "$role:$roleHint" - // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing - // configuration carry over - components[key]?.getChild("configuration")?.let { - component.addChild(it) - } - - val requirements = component.getChild("requirements") - if (requirements != null && requirements.childCount > 0) { - for (r in requirements.childCount - 1 downTo 0) { - val requirement = requirements.getChild(r) - var requiredRole: String? = getValue(requirement, "role") - requiredRole = getRelocatedClass(requiredRole, context) - setValue(requirement, "role", requiredRole) - } - } - components[key] = component + // Only try to merge in components if there are some elements in the component-set + if (newDom.getChild("components") == null) return + + val children = newDom.getChild("components").getChildren("component") + for (component in children) { + var role: String? = getValue(component, "role") + role = getRelocatedClass(role, context) + setValue(component, "role", role) + + val roleHint = getValue(component, "role-hint") + + var impl: String? = getValue(component, "implementation") + impl = getRelocatedClass(impl, context) + setValue(component, "implementation", impl) + + val key = "$role:$roleHint" + // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing + // configuration carry over + components[key]?.getChild("configuration")?.let { + component.addChild(it) + } + + val requirements = component.getChild("requirements") + if (requirements != null && requirements.childCount > 0) { + for (r in requirements.childCount - 1 downTo 0) { + val requirement = requirements.getChild(r) + var requiredRole: String? = getValue(requirement, "role") + requiredRole = getRelocatedClass(requiredRole, context) + setValue(requirement, "role", requiredRole) } + } + components[key] = component } + } - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(COMPONENTS_XML_PATH) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(COMPONENTS_XML_PATH) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) - transformedResource.inputStream().use { - it.copyTo(os) - } - components.clear() + transformedResource.inputStream().use { + it.copyTo(os) } - - override fun hasTransformedResource(): Boolean = components.isNotEmpty() - - @get:Throws(IOException::class) - private val transformedResource: ByteArray - get() { - val os = ByteArrayOutputStream(1024 * 4) - XmlStreamWriter(os).use { writer -> - val dom = Xpp3Dom("component-set") - val componentDom = Xpp3Dom("components") - dom.addChild(componentDom) - for (component in components.values) { - componentDom.addChild(component) - } - Xpp3DomWriter.write(writer, dom) - } - return os.toByteArray() + components.clear() + } + + override fun hasTransformedResource(): Boolean = components.isNotEmpty() + + @get:Throws(IOException::class) + private val transformedResource: ByteArray + get() { + val os = ByteArrayOutputStream(1024 * 4) + XmlStreamWriter(os).use { writer -> + val dom = Xpp3Dom("component-set") + val componentDom = Xpp3Dom("components") + dom.addChild(componentDom) + for (component in components.values) { + componentDom.addChild(component) } + Xpp3DomWriter.write(writer, dom) + } + return os.toByteArray() + } - companion object { - const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" - - private fun getRelocatedClass(className: String?, context: TransformerContext): String? { - val stats = context.stats - if (!className.isNullOrEmpty()) { - for (relocator in context.relocators) { - if (relocator.canRelocateClass(className)) { - val relocateClassContext = RelocateClassContext(className, stats) - return relocator.relocateClass(relocateClassContext) - } - } - } - return className + companion object { + const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" + + private fun getRelocatedClass(className: String?, context: TransformerContext): String? { + val stats = context.stats + if (!className.isNullOrEmpty()) { + for (relocator in context.relocators) { + if (relocator.canRelocateClass(className)) { + val relocateClassContext = RelocateClassContext(className, stats) + return relocator.relocateClass(relocateClassContext) + } } + } + return className + } - private fun getValue(dom: Xpp3Dom, element: String): String { - return dom.getChild(element).value.orEmpty() - } + private fun getValue(dom: Xpp3Dom, element: String): String { + return dom.getChild(element).value.orEmpty() + } - private fun setValue(dom: Xpp3Dom, element: String, value: String?) { - val child = dom.getChild(element) - if (child == null || value.isNullOrEmpty()) return - child.value = value - } + private fun setValue(dom: Xpp3Dom, element: String, value: String?) { + val child = dom.getChild(element) + if (child == null || value.isNullOrEmpty()) return + child.value = value } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index b11ed219b..15ac00db7 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -12,12 +12,12 @@ import org.gradle.api.tasks.Optional * @author John Engelman */ open class DontIncludeResourceTransformer : Transformer by NoOpTransformer { - @get:Optional - @get:Input - var resource: String? = null + @get:Optional + @get:Input + var resource: String? = null - override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString - return !resource.isNullOrEmpty() && path.endsWith(resource!!) - } + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return !resource.isNullOrEmpty() && path.endsWith(resource!!) + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index 6b6e79687..e5bbce6d6 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -26,81 +26,81 @@ import org.gradle.api.file.FileTreeElement */ @CacheableTransformer open class GroovyExtensionModuleTransformer : Transformer { - private val module = Properties() + private val module = Properties() - /** - * default to Groovy 2.4 or earlier - */ - private var legacy = true + /** + * default to Groovy 2.4 or earlier + */ + private var legacy = true - override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString - if (path == GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH) { - // Groovy 2.5+ - legacy = false - return true - } - return path == GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + if (path == GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH) { + // Groovy 2.5+ + legacy = false + return true } + return path == GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH + } - override fun transform(context: TransformerContext) { - val props = Properties() - props.load(context.inputStream) - props.forEach { key, value -> - when (key as String) { - MODULE_NAME_KEY -> handle(key, value as String) { - module.setProperty(key, MERGED_MODULE_NAME) - } - MODULE_VERSION_KEY -> handle(key, value as String) { - module.setProperty(key, MERGED_MODULE_VERSION) - } - EXTENSION_CLASSES_KEY, - STATIC_EXTENSION_CLASSES_KEY, - -> handle(key, value as String) { existingValue -> - module.setProperty(key, "$existingValue,$value") - } - } + override fun transform(context: TransformerContext) { + val props = Properties() + props.load(context.inputStream) + props.forEach { key, value -> + when (key as String) { + MODULE_NAME_KEY -> handle(key, value as String) { + module.setProperty(key, MERGED_MODULE_NAME) + } + MODULE_VERSION_KEY -> handle(key, value as String) { + module.setProperty(key, MERGED_MODULE_VERSION) } + EXTENSION_CLASSES_KEY, + STATIC_EXTENSION_CLASSES_KEY, + -> handle(key, value as String) { existingValue -> + module.setProperty(key, "$existingValue,$value") + } + } } + } - private fun handle(key: String, value: String, mergeValue: (String) -> Unit) { - val existingValue = module.getProperty(key) - if (existingValue != null) { - mergeValue(existingValue) - } else { - module.setProperty(key, value) - } + private fun handle(key: String, value: String, mergeValue: (String) -> Unit) { + val existingValue = module.getProperty(key) + if (existingValue != null) { + mergeValue(existingValue) + } else { + module.setProperty(key, value) } + } - override fun hasTransformedResource(): Boolean = module.isNotEmpty() + override fun hasTransformedResource(): Boolean = module.isNotEmpty() - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val name = if (legacy) GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH else GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH - val entry = ZipEntry(name) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - module.inputStream().use { - it.copyTo(os) - } - os.closeEntry() + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val name = if (legacy) GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH else GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH + val entry = ZipEntry(name) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + module.inputStream().use { + it.copyTo(os) } + os.closeEntry() + } - private companion object { - private fun Properties.inputStream(): InputStream { - val os = ByteArrayOutputStream() - store(os, null) - return ByteArrayInputStream(os.toByteArray()) - } - - private const val GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH = - "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" - private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH = - "META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule" - private const val MODULE_NAME_KEY = "moduleName" - private const val MODULE_VERSION_KEY = "moduleVersion" - private const val EXTENSION_CLASSES_KEY = "extensionClasses" - private const val STATIC_EXTENSION_CLASSES_KEY = "staticExtensionClasses" - private const val MERGED_MODULE_NAME = "MergedByShadowJar" - private const val MERGED_MODULE_VERSION = "1.0.0" + private companion object { + private fun Properties.inputStream(): InputStream { + val os = ByteArrayOutputStream() + store(os, null) + return ByteArrayInputStream(os.toByteArray()) } + + private const val GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH = + "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" + private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH = + "META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule" + private const val MODULE_NAME_KEY = "moduleName" + private const val MODULE_VERSION_KEY = "moduleVersion" + private const val EXTENSION_CLASSES_KEY = "extensionClasses" + private const val STATIC_EXTENSION_CLASSES_KEY = "staticExtensionClasses" + private const val MERGED_MODULE_NAME = "MergedByShadowJar" + private const val MERGED_MODULE_VERSION = "1.0.0" + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index 53540fc54..fa69cf8d4 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -18,22 +18,22 @@ import org.gradle.api.tasks.PathSensitivity * @author John Engelman */ open class IncludeResourceTransformer : Transformer by NoOpTransformer { - @get:InputFile - @get:PathSensitive(PathSensitivity.NONE) - var file: File? = null + @get:InputFile + @get:PathSensitive(PathSensitivity.NONE) + var file: File? = null - @get:Input - var resource: String? = null + @get:Input + var resource: String? = null - override fun hasTransformedResource(): Boolean = file?.exists() == true + override fun hasTransformedResource(): Boolean = file?.exists() == true - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(requireNotNull(resource)) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(requireNotNull(resource)) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) - requireNotNull(file).inputStream().use { inputStream -> - inputStream.copyTo(os) - } + requireNotNull(file).inputStream().use { inputStream -> + inputStream.copyTo(os) } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index 37f565fe9..53e3cbf19 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -25,66 +25,66 @@ import org.gradle.api.file.FileTreeElement */ @CacheableTransformer open class Log4j2PluginsCacheFileTransformer : Transformer { - private val temporaryFiles = mutableListOf() - private val relocators = mutableListOf() - private var stats: ShadowStats? = null + private val temporaryFiles = mutableListOf() + private val relocators = mutableListOf() + private var stats: ShadowStats? = null - override fun canTransformResource(element: FileTreeElement): Boolean { - return PluginProcessor.PLUGIN_CACHE_FILE == element.name - } + override fun canTransformResource(element: FileTreeElement): Boolean { + return PluginProcessor.PLUGIN_CACHE_FILE == element.name + } - override fun transform(context: TransformerContext) { - val temporaryFile = File.createTempFile("Log4j2Plugins", ".dat") - temporaryFile.deleteOnExit() - temporaryFiles.add(temporaryFile) - val fos = temporaryFile.outputStream() - context.inputStream.use { - it.copyTo(fos) - } + override fun transform(context: TransformerContext) { + val temporaryFile = File.createTempFile("Log4j2Plugins", ".dat") + temporaryFile.deleteOnExit() + temporaryFiles.add(temporaryFile) + val fos = temporaryFile.outputStream() + context.inputStream.use { + it.copyTo(fos) + } - relocators.addAll(context.relocators) + relocators.addAll(context.relocators) - if (stats == null) { - stats = context.stats - } + if (stats == null) { + stats = context.stats } + } - override fun hasTransformedResource(): Boolean { - // This functionality matches the original plugin, however, I'm not clear what - // the exact logic is. From what I can tell temporaryFiles should be never be empty - // if anything has been performed. - val hasTransformedMultipleFiles = temporaryFiles.size > 1 - val hasAtLeastOneFileAndRelocator = temporaryFiles.isNotEmpty() && relocators.isNotEmpty() - return hasTransformedMultipleFiles || hasAtLeastOneFileAndRelocator - } + override fun hasTransformedResource(): Boolean { + // This functionality matches the original plugin, however, I'm not clear what + // the exact logic is. From what I can tell temporaryFiles should be never be empty + // if anything has been performed. + val hasTransformedMultipleFiles = temporaryFiles.size > 1 + val hasAtLeastOneFileAndRelocator = temporaryFiles.isNotEmpty() && relocators.isNotEmpty() + return hasTransformedMultipleFiles || hasAtLeastOneFileAndRelocator + } - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val pluginCache = PluginCache() - pluginCache.loadCacheFiles(urlEnumeration) - relocatePlugins(pluginCache) - val entry = ZipEntry(PluginProcessor.PLUGIN_CACHE_FILE) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - pluginCache.writeCache(CloseShieldOutputStream.wrap(os)) - temporaryFiles.clear() - } + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val pluginCache = PluginCache() + pluginCache.loadCacheFiles(urlEnumeration) + relocatePlugins(pluginCache) + val entry = ZipEntry(PluginProcessor.PLUGIN_CACHE_FILE) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + pluginCache.writeCache(CloseShieldOutputStream.wrap(os)) + temporaryFiles.clear() + } - private fun relocatePlugins(pluginCache: PluginCache) { - pluginCache.allCategories.values.forEach { currentMap -> - currentMap.values.forEach { currentPluginEntry -> - val className = currentPluginEntry.className - val relocateClassContext = RelocateClassContext(className, requireNotNull(stats)) - relocators.firstOrNull { it.canRelocateClass(className) }?.let { relocator -> - // Then we perform that relocation and update the plugin entry to reflect the new value. - currentPluginEntry.className = relocator.relocateClass(relocateClassContext) - } - } + private fun relocatePlugins(pluginCache: PluginCache) { + pluginCache.allCategories.values.forEach { currentMap -> + currentMap.values.forEach { currentPluginEntry -> + val className = currentPluginEntry.className + val relocateClassContext = RelocateClassContext(className, requireNotNull(stats)) + relocators.firstOrNull { it.canRelocateClass(className) }?.let { relocator -> + // Then we perform that relocation and update the plugin entry to reflect the new value. + currentPluginEntry.className = relocator.relocateClass(relocateClassContext) } + } } + } - private val urlEnumeration: Enumeration - get() { - val urls = temporaryFiles.map { it.toURI().toURL() } - return Collections.enumeration(urls) - } + private val urlEnumeration: Enumeration + get() { + val urls = temporaryFiles.map { it.toURI().toURL() } + return Collections.enumeration(urls) + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index e925cc08b..6e1965ba5 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -18,57 +18,57 @@ import org.slf4j.LoggerFactory * @author Chris Rankin */ open class ManifestAppenderTransformer : Transformer { - private var manifestContents = ByteArray(0) - private val _attributes = mutableListOf>>() + private var manifestContents = ByteArray(0) + private val _attributes = mutableListOf>>() - @get:Input - open val attributes: List>> get() = _attributes + @get:Input + open val attributes: List>> get() = _attributes - override fun canTransformResource(element: FileTreeElement): Boolean { - return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) - } + override fun canTransformResource(element: FileTreeElement): Boolean { + return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) + } - override fun transform(context: TransformerContext) { - if (manifestContents.isEmpty()) { - try { - context.inputStream.use { inputStream -> - val outputStream = ByteArrayOutputStream() - inputStream.copyTo(outputStream) - manifestContents = outputStream.toByteArray() - } - } catch (e: IOException) { - logger.warn("Failed to read MANIFEST.MF", e) - } + override fun transform(context: TransformerContext) { + if (manifestContents.isEmpty()) { + try { + context.inputStream.use { inputStream -> + val outputStream = ByteArrayOutputStream() + inputStream.copyTo(outputStream) + manifestContents = outputStream.toByteArray() } + } catch (e: IOException) { + logger.warn("Failed to read MANIFEST.MF", e) + } } + } - override fun hasTransformedResource(): Boolean = _attributes.isNotEmpty() + override fun hasTransformedResource(): Boolean = _attributes.isNotEmpty() - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(MANIFEST_NAME) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - os.write(manifestContents) + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(MANIFEST_NAME) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + os.write(manifestContents) - if (_attributes.isNotEmpty()) { - for ((key, value) in _attributes) { - os.write(key.toByteArray()) - os.write(SEPARATOR) - os.write(value.toString().toByteArray()) - os.write(EOL) - } - os.write(EOL) - _attributes.clear() - } + if (_attributes.isNotEmpty()) { + for ((key, value) in _attributes) { + os.write(key.toByteArray()) + os.write(SEPARATOR) + os.write(value.toString().toByteArray()) + os.write(EOL) + } + os.write(EOL) + _attributes.clear() } + } - open fun append(name: String, value: Comparable<*>): ManifestAppenderTransformer = apply { - _attributes.add(Pair(name, value)) - } + open fun append(name: String, value: Comparable<*>): ManifestAppenderTransformer = apply { + _attributes.add(Pair(name, value)) + } - private companion object { - private val logger = LoggerFactory.getLogger(ManifestAppenderTransformer::class.java) - private val EOL = "\r\n".toByteArray() - private val SEPARATOR = ": ".toByteArray() - } + private companion object { + private val logger = LoggerFactory.getLogger(ManifestAppenderTransformer::class.java) + private val EOL = "\r\n".toByteArray() + private val SEPARATOR = ": ".toByteArray() + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index fb2ced9ce..900e4cb98 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -23,66 +23,66 @@ import org.slf4j.LoggerFactory * @author John Engelman */ open class ManifestResourceTransformer : Transformer { - private var manifestDiscovered = false - private var manifest: Manifest? = null + private var manifestDiscovered = false + private var manifest: Manifest? = null - @get:Optional - @get:Input - var mainClass: String? = null + @get:Optional + @get:Input + var mainClass: String? = null - @get:Optional - @get:Input - var manifestEntries: MutableMap? = null + @get:Optional + @get:Input + var manifestEntries: MutableMap? = null - override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString - return JarFile.MANIFEST_NAME.equals(path, ignoreCase = true) - } + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + return JarFile.MANIFEST_NAME.equals(path, ignoreCase = true) + } - override fun transform(context: TransformerContext) { - // We just want to take the first manifest we come across as that's our project's manifest. This is the behavior - // now which is situational at best. Right now there is no context passed in with the processing so we cannot - // tell what artifact is being processed. - if (!manifestDiscovered) { - try { - manifest = Manifest(context.inputStream) - manifestDiscovered = true - } catch (e: IOException) { - logger.warn("Failed to read MANIFEST.MF", e) - } - } + override fun transform(context: TransformerContext) { + // We just want to take the first manifest we come across as that's our project's manifest. This is the behavior + // now which is situational at best. Right now there is no context passed in with the processing so we cannot + // tell what artifact is being processed. + if (!manifestDiscovered) { + try { + manifest = Manifest(context.inputStream) + manifestDiscovered = true + } catch (e: IOException) { + logger.warn("Failed to read MANIFEST.MF", e) + } } + } - override fun hasTransformedResource(): Boolean = true - - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - // If we didn't find a manifest, then let's create one. - if (manifest == null) { - manifest = Manifest() - } - - val attributes = manifest!!.mainAttributes - mainClass?.let { - attributes[Attributes.Name.MAIN_CLASS] = it - } - manifestEntries?.forEach { (key, value) -> - attributes[Attributes.Name(key)] = value - } + override fun hasTransformedResource(): Boolean = true - val entry = ZipEntry(JarFile.MANIFEST_NAME) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - manifest!!.write(os) + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + // If we didn't find a manifest, then let's create one. + if (manifest == null) { + manifest = Manifest() } - open fun attributes(attributes: Map): ManifestResourceTransformer = apply { - if (manifestEntries == null) { - manifestEntries = LinkedHashMap() - } - manifestEntries!!.putAll(attributes) + val attributes = manifest!!.mainAttributes + mainClass?.let { + attributes[Attributes.Name.MAIN_CLASS] = it } + manifestEntries?.forEach { (key, value) -> + attributes[Attributes.Name(key)] = value + } + + val entry = ZipEntry(JarFile.MANIFEST_NAME) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + manifest!!.write(os) + } - private companion object { - private val logger = LoggerFactory.getLogger(ManifestResourceTransformer::class.java) + open fun attributes(attributes: Map): ManifestResourceTransformer = apply { + if (manifestEntries == null) { + manifestEntries = LinkedHashMap() } + manifestEntries!!.putAll(attributes) + } + + private companion object { + private val logger = LoggerFactory.getLogger(ManifestResourceTransformer::class.java) + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index ba5a2632b..2501f04f0 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -90,138 +90,138 @@ import org.gradle.api.tasks.Internal * @author Marc Philipp */ open class PropertiesFileTransformer : Transformer { - private val propertiesEntries = mutableMapOf() - private val _charset get() = Charset.forName(charset) - - @get:Input - var paths: List = listOf() - - @get:Input - var mappings: Map> = mapOf() - - /** - * Optional values: first, latest, append. - */ - @get:Input - var mergeStrategy: String = "first" - - @get:Input - var mergeSeparator: String = "," - - @get:Input - var charset: String = "ISO_8859_1" - - /** - * Use [java.util.function.Function] here for compatibility with Groovy and Java. - */ - @get:Internal - var keyTransformer: Function = IDENTITY - - override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString - if (path in mappings) return true - for (key in mappings.keys) { - if (key.toRegex().containsMatchIn(path)) return true - } - if (path in paths) return true - for (p in paths) { - if (p.toRegex().containsMatchIn(path)) return true - } - return mappings.isEmpty() && paths.isEmpty() && path.endsWith(PROPERTIES_SUFFIX) + private val propertiesEntries = mutableMapOf() + private val _charset get() = Charset.forName(charset) + + @get:Input + var paths: List = listOf() + + @get:Input + var mappings: Map> = mapOf() + + /** + * Optional values: first, latest, append. + */ + @get:Input + var mergeStrategy: String = "first" + + @get:Input + var mergeSeparator: String = "," + + @get:Input + var charset: String = "ISO_8859_1" + + /** + * Use [java.util.function.Function] here for compatibility with Groovy and Java. + */ + @get:Internal + var keyTransformer: Function = IDENTITY + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.relativePath.pathString + if (path in mappings) return true + for (key in mappings.keys) { + if (key.toRegex().containsMatchIn(path)) return true } - - override fun transform(context: TransformerContext) { - val props = propertiesEntries[context.path] - val incoming = loadAndTransformKeys(context.inputStream) - if (props == null) { - propertiesEntries[context.path] = incoming + if (path in paths) return true + for (p in paths) { + if (p.toRegex().containsMatchIn(path)) return true + } + return mappings.isEmpty() && paths.isEmpty() && path.endsWith(PROPERTIES_SUFFIX) + } + + override fun transform(context: TransformerContext) { + val props = propertiesEntries[context.path] + val incoming = loadAndTransformKeys(context.inputStream) + if (props == null) { + propertiesEntries[context.path] = incoming + } else { + for ((key, value) in incoming) { + if (props.containsKey(key)) { + when (mergeStrategyFor(context.path).lowercase()) { + "latest" -> props[key] = value + "append" -> props[key] = props.getProperty(key as String) + mergeSeparatorFor(context.path) + value + "first" -> Unit + else -> Unit + } } else { - for ((key, value) in incoming) { - if (props.containsKey(key)) { - when (mergeStrategyFor(context.path).lowercase()) { - "latest" -> props[key] = value - "append" -> props[key] = props.getProperty(key as String) + mergeSeparatorFor(context.path) + value - "first" -> Unit - else -> Unit - } - } else { - props[key] = value - } - } + props[key] = value } + } } - - private fun loadAndTransformKeys(inputStream: InputStream): CleanProperties { - val props = CleanProperties() - // InputStream closed by caller, so we don't do it here. - props.load(inputStream.reader(_charset)) - return transformKeys(props) + } + + private fun loadAndTransformKeys(inputStream: InputStream): CleanProperties { + val props = CleanProperties() + // InputStream closed by caller, so we don't do it here. + props.load(inputStream.reader(_charset)) + return transformKeys(props) + } + + private fun transformKeys(properties: Properties): CleanProperties { + if (keyTransformer === IDENTITY) { + return properties as CleanProperties } - - private fun transformKeys(properties: Properties): CleanProperties { - if (keyTransformer === IDENTITY) { - return properties as CleanProperties - } - val result = CleanProperties() - properties.forEach { (key, value) -> - result[keyTransformer.apply(key as String)] = value - } - return result + val result = CleanProperties() + properties.forEach { (key, value) -> + result[keyTransformer.apply(key as String)] = value } + return result + } - private fun mergeStrategyFor(path: String): String { - mappings[path]?.let { - return it["mergeStrategy"] ?: mergeStrategy - } - for (key in mappings.keys) { - if (key.toRegex().containsMatchIn(path)) { - return mappings[key]?.get("mergeStrategy") ?: mergeStrategy - } - } - return mergeStrategy + private fun mergeStrategyFor(path: String): String { + mappings[path]?.let { + return it["mergeStrategy"] ?: mergeStrategy } - - private fun mergeSeparatorFor(path: String): String { - mappings[path]?.let { - return it["mergeSeparator"] ?: mergeSeparator - } - for (key in mappings.keys) { - if (key.toRegex().containsMatchIn(path)) { - return mappings[key]?.get("mergeSeparator") ?: mergeSeparator - } - } - return mergeSeparator + for (key in mappings.keys) { + if (key.toRegex().containsMatchIn(path)) { + return mappings[key]?.get("mergeStrategy") ?: mergeStrategy + } } + return mergeStrategy + } - override fun hasTransformedResource(): Boolean { - return propertiesEntries.isNotEmpty() + private fun mergeSeparatorFor(path: String): String { + mappings[path]?.let { + return it["mergeSeparator"] ?: mergeSeparator } - - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - // cannot close the writer as the OutputStream needs to remain open - val zipWriter = os.writer(_charset) - propertiesEntries.forEach { (path, props) -> - val entry = ZipEntry(path) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - props.toReader().use { - it.copyTo(zipWriter) - } - zipWriter.flush() - os.closeEntry() - } + for (key in mappings.keys) { + if (key.toRegex().containsMatchIn(path)) { + return mappings[key]?.get("mergeSeparator") ?: mergeSeparator + } } - - private fun Properties.toReader(): InputStreamReader { - val os = ByteArrayOutputStream() - os.writer(Charset.forName(charset)).use { writer -> - store(writer, "") - } - return os.toByteArray().inputStream().reader(_charset) + return mergeSeparator + } + + override fun hasTransformedResource(): Boolean { + return propertiesEntries.isNotEmpty() + } + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + // cannot close the writer as the OutputStream needs to remain open + val zipWriter = os.writer(_charset) + propertiesEntries.forEach { (path, props) -> + val entry = ZipEntry(path) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + props.toReader().use { + it.copyTo(zipWriter) + } + zipWriter.flush() + os.closeEntry() } + } - private companion object { - private const val PROPERTIES_SUFFIX = ".properties" - private val IDENTITY = Function { it } + private fun Properties.toReader(): InputStreamReader { + val os = ByteArrayOutputStream() + os.writer(Charset.forName(charset)).use { writer -> + store(writer, "") } + return os.toByteArray().inputStream().reader(_charset) + } + + private companion object { + private const val PROPERTIES_SUFFIX = ".properties" + private val IDENTITY = Function { it } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 824ac063a..d2f4e37db 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -29,88 +29,88 @@ import org.gradle.api.tasks.util.PatternSet */ @CacheableTransformer open class ServiceFileTransformer( - private val patternSet: PatternSet = PatternSet() - .include(SERVICES_PATTERN) - .exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN), + private val patternSet: PatternSet = PatternSet() + .include(SERVICES_PATTERN) + .exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN), ) : Transformer, - PatternFilterable by patternSet { - private val serviceEntries = mutableMapOf() + PatternFilterable by patternSet { + private val serviceEntries = mutableMapOf() - override fun canTransformResource(element: FileTreeElement): Boolean { - val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element - return patternSet.asSpec.isSatisfiedBy(target) - } + override fun canTransformResource(element: FileTreeElement): Boolean { + val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element + return patternSet.asSpec.isSatisfiedBy(target) + } - override fun transform(context: TransformerContext) { - val lines = context.inputStream.bufferedReader().readLines().toMutableList() - var targetPath = context.path - context.relocators.forEach { rel -> - if (rel.canRelocateClass(File(targetPath).name)) { - val classContext = RelocateClassContext.builder().className(targetPath).stats(context.stats).build() - targetPath = rel.relocateClass(classContext) - } - lines.forEachIndexed { i, line -> - if (rel.canRelocateClass(line)) { - val lineContext = RelocateClassContext.builder().className(line).stats(context.stats).build() - lines[i] = rel.relocateClass(lineContext) - } - } - } - lines.forEach { line -> - serviceEntries[targetPath] = serviceEntries.getOrDefault(targetPath, ServiceStream()).apply { - append(ByteArrayInputStream(line.toByteArray())) - } + override fun transform(context: TransformerContext) { + val lines = context.inputStream.bufferedReader().readLines().toMutableList() + var targetPath = context.path + context.relocators.forEach { rel -> + if (rel.canRelocateClass(File(targetPath).name)) { + val classContext = RelocateClassContext.builder().className(targetPath).stats(context.stats).build() + targetPath = rel.relocateClass(classContext) + } + lines.forEachIndexed { i, line -> + if (rel.canRelocateClass(line)) { + val lineContext = RelocateClassContext.builder().className(line).stats(context.stats).build() + lines[i] = rel.relocateClass(lineContext) } + } + } + lines.forEach { line -> + serviceEntries[targetPath] = serviceEntries.getOrDefault(targetPath, ServiceStream()).apply { + append(ByteArrayInputStream(line.toByteArray())) + } } + } - override fun hasTransformedResource(): Boolean = serviceEntries.isNotEmpty() + override fun hasTransformedResource(): Boolean = serviceEntries.isNotEmpty() - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - serviceEntries.forEach { (path, stream) -> - val entry = ZipEntry(path) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - stream.toInputStream().use { - it.copyTo(os) - } - os.closeEntry() - } + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + serviceEntries.forEach { (path, stream) -> + val entry = ZipEntry(path) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + stream.toInputStream().use { + it.copyTo(os) + } + os.closeEntry() } + } - /** - * {@inheritDoc} - */ - @Input - override fun getIncludes(): Set = patternSet.includes + /** + * {@inheritDoc} + */ + @Input + override fun getIncludes(): Set = patternSet.includes - /** - * {@inheritDoc} - */ - @Input - override fun getExcludes(): Set = patternSet.excludes + /** + * {@inheritDoc} + */ + @Input + override fun getExcludes(): Set = patternSet.excludes - open fun setPath(path: String): PatternFilterable = apply { - patternSet.setIncludes(listOf("$path/**")) - } + open fun setPath(path: String): PatternFilterable = apply { + patternSet.setIncludes(listOf("$path/**")) + } - open class ServiceStream : ByteArrayOutputStream(1024) { - @Throws(IOException::class) - open fun append(inputStream: InputStream) { - if (count > 0 && buf[count - 1] != '\n'.code.toByte() && buf[count - 1] != '\r'.code.toByte()) { - val newline = "\n".toByteArray() - write(newline, 0, newline.size) - } - inputStream.use { - it.copyTo(this) - } - } - - open fun toInputStream(): InputStream = ByteArrayInputStream(buf, 0, count) + open class ServiceStream : ByteArrayOutputStream(1024) { + @Throws(IOException::class) + open fun append(inputStream: InputStream) { + if (count > 0 && buf[count - 1] != '\n'.code.toByte() && buf[count - 1] != '\r'.code.toByte()) { + val newline = "\n".toByteArray() + write(newline, 0, newline.size) + } + inputStream.use { + it.copyTo(this) + } } - private companion object { - private const val SERVICES_PATTERN = "META-INF/services/**" - private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN = - "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" - } + open fun toInputStream(): InputStream = ByteArrayInputStream(buf, 0, count) + } + + private companion object { + private const val SERVICES_PATTERN = "META-INF/services/**" + private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN = + "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index 9a2aa5e5c..6039d8db4 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -13,21 +13,21 @@ import org.gradle.api.tasks.Internal * @author John Engelman */ interface Transformer : Named { - fun canTransformResource(element: FileTreeElement): Boolean + fun canTransformResource(element: FileTreeElement): Boolean - fun transform(context: TransformerContext) + fun transform(context: TransformerContext) - fun hasTransformedResource(): Boolean + fun hasTransformedResource(): Boolean - fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) + fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) - @Internal - override fun getName(): String = this::class.java.simpleName + @Internal + override fun getName(): String = this::class.java.simpleName } object NoOpTransformer : Transformer { - override fun canTransformResource(element: FileTreeElement): Boolean = false - override fun transform(context: TransformerContext): Unit = Unit - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean): Unit = Unit - override fun hasTransformedResource(): Boolean = false + override fun canTransformResource(element: FileTreeElement): Boolean = false + override fun transform(context: TransformerContext): Unit = Unit + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean): Unit = Unit + override fun hasTransformedResource(): Boolean = false } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt index f80f19392..00a7801f9 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -6,36 +6,36 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import java.io.InputStream data class TransformerContext @JvmOverloads constructor( - val path: String, - val inputStream: InputStream, - val relocators: List = emptyList(), - val stats: ShadowStats = ShadowStats(), + val path: String, + val inputStream: InputStream, + val relocators: List = emptyList(), + val stats: ShadowStats = ShadowStats(), ) { - class Builder { - private var path = "" - private var inputStream: InputStream? = null - private var relocators = emptyList() - private var stats = ShadowStats() + class Builder { + private var path = "" + private var inputStream: InputStream? = null + private var relocators = emptyList() + private var stats = ShadowStats() - fun path(path: String): Builder = apply { this.path = path } - fun inputStream(inputStream: InputStream): Builder = apply { this.inputStream = inputStream } - fun relocators(relocators: List): Builder = apply { this.relocators = relocators } - fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } - fun build(): TransformerContext = TransformerContext( - path = path, - inputStream = inputStream ?: error("inputStream is required"), - relocators = relocators, - stats = stats, - ) - } + fun path(path: String): Builder = apply { this.path = path } + fun inputStream(inputStream: InputStream): Builder = apply { this.inputStream = inputStream } + fun relocators(relocators: List): Builder = apply { this.relocators = relocators } + fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + fun build(): TransformerContext = TransformerContext( + path = path, + inputStream = inputStream ?: error("inputStream is required"), + relocators = relocators, + stats = stats, + ) + } - companion object { - @JvmStatic - fun builder(): Builder = Builder() + companion object { + @JvmStatic + fun builder(): Builder = Builder() - @JvmStatic - fun getEntryTimestamp(preserveFileTimestamps: Boolean, entryTime: Long): Long { - return if (preserveFileTimestamps) entryTime else ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES - } + @JvmStatic + fun getEntryTimestamp(preserveFileTimestamps: Boolean, entryTime: Long): Long { + return if (preserveFileTimestamps) entryTime else ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES } + } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index 1b5b6d0f8..b0d74d03b 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -24,59 +24,59 @@ import org.xml.sax.InputSource */ @CacheableTransformer open class XmlAppendingTransformer : Transformer { - private var doc: Document? = null + private var doc: Document? = null - @get:Input - var ignoreDtd: Boolean = true + @get:Input + var ignoreDtd: Boolean = true - @get:Optional - @get:Input - var resource: String? = null + @get:Optional + @get:Input + var resource: String? = null - override fun canTransformResource(element: FileTreeElement): Boolean { - return resource?.equals(element.relativePath.pathString, ignoreCase = true) == true - } + override fun canTransformResource(element: FileTreeElement): Boolean { + return resource?.equals(element.relativePath.pathString, ignoreCase = true) == true + } - override fun transform(context: TransformerContext) { - val r = try { - SAXBuilder(XMLReaders.NONVALIDATING).apply { - expandEntities = false - if (ignoreDtd) { - entityResolver = EntityResolver { _, _ -> InputSource(StringReader("")) } - } - }.build(context.inputStream) - } catch (e: JDOMException) { - throw RuntimeException("Error processing resource $resource: ${e.message}", e) + override fun transform(context: TransformerContext) { + val r = try { + SAXBuilder(XMLReaders.NONVALIDATING).apply { + expandEntities = false + if (ignoreDtd) { + entityResolver = EntityResolver { _, _ -> InputSource(StringReader("")) } } + }.build(context.inputStream) + } catch (e: JDOMException) { + throw RuntimeException("Error processing resource $resource: ${e.message}", e) + } - if (doc == null) { - doc = r - } else { - val root = r.rootElement - root.attributes.forEach { a -> - val mergedEl = doc!!.rootElement - val mergedAtt = mergedEl.getAttribute(a.name, a.namespace) - if (mergedAtt == null) { - mergedEl.setAttribute(a) - } - } - root.children.forEach { n -> - doc!!.rootElement.addContent(n.clone()) - } + if (doc == null) { + doc = r + } else { + val root = r.rootElement + root.attributes.forEach { a -> + val mergedEl = doc!!.rootElement + val mergedAtt = mergedEl.getAttribute(a.name, a.namespace) + if (mergedAtt == null) { + mergedEl.setAttribute(a) } + } + root.children.forEach { n -> + doc!!.rootElement.addContent(n.clone()) + } } + } - override fun hasTransformedResource(): Boolean = doc != null + override fun hasTransformedResource(): Boolean = doc != null - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(resource) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - XMLOutputter(Format.getPrettyFormat()).output(doc, os) - doc = null - } + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val entry = ZipEntry(resource) + entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + XMLOutputter(Format.getPrettyFormat()).output(doc, os) + doc = null + } - companion object { - const val XSI_NS: String = "http://www.w3.org/2001/XMLSchema-instance" - } + companion object { + const val XSI_NS: String = "http://www.w3.org/2001/XMLSchema-instance" + } } From 4397a27f4dd5339e121e211d26750c12f34cf9fb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 10:20:22 -0500 Subject: [PATCH 015/941] Enable Explicit API mode (#1031) --- api/build.gradle.kts | 1 + .../plugins/shadow/ShadowApplicationPlugin.kt | 10 +++--- .../gradle/plugins/shadow/ShadowBasePlugin.kt | 16 ++++----- .../gradle/plugins/shadow/ShadowExtension.kt | 4 +-- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 8 ++--- .../gradle/plugins/shadow/ShadowPlugin.kt | 2 +- .../gradle/plugins/shadow/ShadowStats.kt | 34 +++++++++--------- .../plugins/shadow/impl/RelocatorRemapper.kt | 8 ++--- .../shadow/internal/DependencyFilter.kt | 20 +++++------ .../plugins/shadow/internal/UnusedTracker.kt | 2 -- .../shadow/legacy/LegacyShadowPlugin.kt | 2 +- .../shadow/relocation/CacheableRelocator.kt | 2 +- .../shadow/relocation/RelocateClassContext.kt | 14 ++++---- .../shadow/relocation/RelocatePathContext.kt | 14 ++++---- .../plugins/shadow/relocation/Relocator.kt | 16 ++++----- .../shadow/relocation/SimpleRelocator.kt | 20 +++++------ .../shadow/tasks/DefaultInheritManifest.kt | 2 +- .../plugins/shadow/tasks/InheritManifest.kt | 6 ++-- .../gradle/plugins/shadow/tasks/KnowsTask.kt | 14 ++++---- .../plugins/shadow/tasks/ShadowCopyAction.kt | 22 ++++++------ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 26 +++++++------- .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 36 +++++++++---------- .../plugins/shadow/tasks/ZipCompressor.kt | 2 +- .../ApacheLicenseResourceTransformer.kt | 2 +- .../ApacheNoticeResourceTransformer.kt | 22 ++++++------ .../transformers/AppendingTransformer.kt | 4 +-- .../transformers/CacheableTransformer.kt | 2 +- .../ComponentsXmlResourceTransformer.kt | 6 ++-- .../DontIncludeResourceTransformer.kt | 4 +-- .../GroovyExtensionModuleTransformer.kt | 2 +- .../IncludeResourceTransformer.kt | 6 ++-- .../Log4j2PluginsCacheFileTransformer.kt | 2 +- .../ManifestAppenderTransformer.kt | 6 ++-- .../ManifestResourceTransformer.kt | 8 ++--- .../transformers/PropertiesFileTransformer.kt | 14 ++++---- .../transformers/ServiceFileTransformer.kt | 10 +++--- .../shadow/transformers/Transformer.kt | 20 +++++------ .../shadow/transformers/TransformerContext.kt | 20 +++++------ .../transformers/XmlAppendingTransformer.kt | 10 +++--- 39 files changed, 209 insertions(+), 210 deletions(-) diff --git a/api/build.gradle.kts b/api/build.gradle.kts index 964159f65..06d3b7a96 100644 --- a/api/build.gradle.kts +++ b/api/build.gradle.kts @@ -13,6 +13,7 @@ java { } kotlin { + explicitApi() compilerOptions { // https://docs.gradle.org/current/userguide/compatibility.html#kotlin apiVersion = KotlinVersion.KOTLIN_1_8 diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 790cfc486..445075c02 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -16,7 +16,7 @@ import org.gradle.api.tasks.application.CreateStartScripts import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator import org.gradle.jvm.toolchain.JavaToolchainService -abstract class ShadowApplicationPlugin : Plugin { +public abstract class ShadowApplicationPlugin : Plugin { private lateinit var project: Project private lateinit var javaApplication: JavaApplication @@ -127,9 +127,9 @@ abstract class ShadowApplicationPlugin : Plugin { protected val shadowJar: TaskProvider get() = project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar::class.java) - companion object { - const val SHADOW_RUN_TASK_NAME: String = "runShadow" - const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" - const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" + public companion object { + public const val SHADOW_RUN_TASK_NAME: String = "runShadow" + public const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" + public const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index c095ae9c3..bdf44ea48 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -6,7 +6,7 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.util.GradleVersion -abstract class ShadowBasePlugin : Plugin { +public abstract class ShadowBasePlugin : Plugin { override fun apply(project: Project) { if (GradleVersion.current() < GradleVersion.version("8.3")) { @@ -20,12 +20,12 @@ abstract class ShadowBasePlugin : Plugin { } } - companion object { - const val SHADOW: String = "shadow" - const val GROUP_NAME: String = SHADOW - const val EXTENSION_NAME: String = SHADOW - const val CONFIGURATION_NAME: String = SHADOW - const val COMPONENT_NAME: String = SHADOW - const val DISTRIBUTION_NAME: String = SHADOW + public companion object { + public const val SHADOW: String = "shadow" + public const val GROUP_NAME: String = SHADOW + public const val EXTENSION_NAME: String = SHADOW + public const val CONFIGURATION_NAME: String = SHADOW + public const val COMPONENT_NAME: String = SHADOW + public const val DISTRIBUTION_NAME: String = SHADOW } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index 871f15d7b..924148c32 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -4,11 +4,11 @@ import org.gradle.api.Project import org.gradle.api.publish.maven.MavenPublication @Deprecated("This is deprecated since 8.3.2") -abstract class ShadowExtension(project: Project) { +public abstract class ShadowExtension(project: Project) { private val components = project.components @Deprecated("configure publication using component.shadow directly.") - fun component(publication: MavenPublication) { + public fun component(publication: MavenPublication) { publication.from(components.findByName(ShadowBasePlugin.COMPONENT_NAME)) } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt index 35dfdf3b7..ca3a1c2dc 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt @@ -17,7 +17,7 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.jvm.tasks.Jar import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin -abstract class ShadowJavaPlugin @Inject constructor( +public abstract class ShadowJavaPlugin @Inject constructor( private val softwareComponentFactory: SoftwareComponentFactory, ) : Plugin { @@ -104,8 +104,8 @@ abstract class ShadowJavaPlugin @Inject constructor( return taskProvider } - companion object { - const val SHADOW_JAR_TASK_NAME: String = "shadowJar" - const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" + public companion object { + public const val SHADOW_JAR_TASK_NAME: String = "shadowJar" + public const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt index 93fce387e..c00756f43 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt @@ -6,7 +6,7 @@ import org.gradle.api.Project import org.gradle.api.plugins.ApplicationPlugin import org.gradle.api.plugins.JavaPlugin -abstract class ShadowPlugin : Plugin { +public abstract class ShadowPlugin : Plugin { override fun apply(project: Project) { project.run { diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt index f59d9a5fd..45406acb7 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt @@ -2,50 +2,50 @@ package com.github.jengelman.gradle.plugins.shadow import org.gradle.api.GradleException -open class ShadowStats { - open var totalTime: Long = 0 - open var jarStartTime: Long = 0 - open var jarEndTime: Long = 0 - open var jarCount: Int = 1 - open var processingJar: Boolean = false - open val relocations: MutableMap = mutableMapOf() +public open class ShadowStats { + public open var totalTime: Long = 0 + public open var jarStartTime: Long = 0 + public open var jarEndTime: Long = 0 + public open var jarCount: Int = 1 + public open var processingJar: Boolean = false + public open val relocations: MutableMap = mutableMapOf() - open val relocationString: String + public open val relocationString: String get() { return relocations.map { (k, v) -> "$k → $v" } .sorted() .joinToString("\n") } - open val jarTiming: Long + public open val jarTiming: Long get() = jarEndTime - jarStartTime - open val totalTimeSecs: Double + public open val totalTimeSecs: Double get() = totalTime / 1000.0 - open val averageTimePerJar: Double + public open val averageTimePerJar: Double get() = totalTime / jarCount.toDouble() - open val averageTimeSecsPerJar: Double + public open val averageTimeSecsPerJar: Double get() = averageTimePerJar / 1000.0 - open val buildScanData: Map + public open val buildScanData: Map get() = mapOf( "dependencies" to jarCount.toString(), "relocations" to relocationString, ) - open fun relocate(src: String, dst: String) { + public open fun relocate(src: String, dst: String) { relocations[src] = dst } - open fun startJar() { + public open fun startJar() { if (processingJar) throw GradleException("Can only time one entry at a time") processingJar = true jarStartTime = System.currentTimeMillis() } - open fun finishJar() { + public open fun finishJar() { if (processingJar) { jarEndTime = System.currentTimeMillis() jarCount++ @@ -54,7 +54,7 @@ open class ShadowStats { } } - open fun printStats() { + public open fun printStats() { println(this) } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt index fc0335803..395fa5bb3 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt @@ -13,13 +13,13 @@ import org.objectweb.asm.commons.Remapper * * @author John Engelman */ -open class RelocatorRemapper( +public open class RelocatorRemapper( private val relocators: List, private val stats: ShadowStats, ) : Remapper() { private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") - open fun hasRelocators(): Boolean = relocators.isNotEmpty() + public open fun hasRelocators(): Boolean = relocators.isNotEmpty() override fun mapValue(value: Any): Any { return if (value is String) { @@ -54,11 +54,11 @@ open class RelocatorRemapper( return name } - open fun mapPath(path: String): String { + public open fun mapPath(path: String): String { return map(path.substring(0, path.indexOf('.'))) } - open fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String { + public open fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String { return mapPath(path.pathString) } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt index b6ddff4b3..c89fa8de9 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt @@ -6,49 +6,49 @@ import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.file.FileCollection import org.gradle.api.specs.Spec -interface DependencyFilter { +public interface DependencyFilter { /** * Resolve a FileCollection against the include/exclude rules in the filter. */ - fun resolve(configuration: FileCollection): FileCollection + public fun resolve(configuration: FileCollection): FileCollection /** * Resolve all FileCollections against the include/exclude rules in the filter and combine the results. */ - fun resolve(configurations: Collection): FileCollection + public fun resolve(configurations: Collection): FileCollection /** * Exclude dependencies that match the provided spec. */ - fun exclude(spec: Spec): DependencyFilter + public fun exclude(spec: Spec): DependencyFilter /** * Include dependencies that match the provided spec. */ - fun include(spec: Spec): DependencyFilter + public fun include(spec: Spec): DependencyFilter /** * Create a spec that matches the provided project notation on group, name, and version. */ - fun project(notation: Map): Spec + public fun project(notation: Map): Spec /** * Create a spec that matches the default configuration for the provided project path on group, name, and version. */ - fun project(notation: String): Spec + public fun project(notation: String): Spec /** * Create a spec that matches dependencies using the provided notation on group, name, and version. */ - fun dependency(notation: Any): Spec + public fun dependency(notation: Any): Spec /** * Create a spec that matches the provided dependency on group, name, and version. */ - fun dependency(dependency: Dependency): Spec + public fun dependency(dependency: Dependency): Spec /** * Create a spec that matches the provided closure. */ - fun dependency(closure: Closure<*>): Spec + public fun dependency(closure: Closure<*>): Spec } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index 269f1b15a..a23072aa4 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -43,7 +43,6 @@ internal class UnusedTracker private constructor( } companion object { - @JvmStatic fun forProject( apiJars: FileCollection, sourceSetsClassesDirs: Iterable, @@ -52,7 +51,6 @@ internal class UnusedTracker private constructor( return UnusedTracker(sourceSetsClassesDirs, apiJars, toMinimize) } - @JvmStatic fun getApiJarsFromProject(project: Project): FileCollection { val apiDependencies = project.configurations.findByName("api")?.dependencies ?: return project.files() diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt index 45441c642..f3e2e205f 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt @@ -8,6 +8,6 @@ import org.gradle.api.Project * * This allows older build logic to keep on working as if that old plugin ID was applied. */ -abstract class LegacyShadowPlugin : Plugin { +public abstract class LegacyShadowPlugin : Plugin { override fun apply(project: Project): Unit = Unit } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt index cde63e424..e78d7a777 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt @@ -7,4 +7,4 @@ package com.github.jengelman.gradle.plugins.shadow.relocation */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS) -annotation class CacheableRelocator +public annotation class CacheableRelocator diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt index ec06e07ff..72f8c8623 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt @@ -2,21 +2,21 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.ShadowStats -data class RelocateClassContext( +public data class RelocateClassContext( val className: String, val stats: ShadowStats, ) { - class Builder { + public class Builder { private var className = "" private var stats = ShadowStats() - fun className(className: String): Builder = apply { this.className = className } - fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } - fun build(): RelocateClassContext = RelocateClassContext(className, stats) + public fun className(className: String): Builder = apply { this.className = className } + public fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + public fun build(): RelocateClassContext = RelocateClassContext(className, stats) } - companion object { + public companion object { @JvmStatic - fun builder(): Builder = Builder() + public fun builder(): Builder = Builder() } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt index a9cf9cce9..63e4655cc 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt @@ -2,21 +2,21 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.ShadowStats -data class RelocatePathContext( +public data class RelocatePathContext( val path: String, val stats: ShadowStats, ) { - class Builder { + public class Builder { private var path = "" private var stats = ShadowStats() - fun path(path: String): Builder = apply { this.path = path } - fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } - fun build(): RelocatePathContext = RelocatePathContext(path, stats) + public fun path(path: String): Builder = apply { this.path = path } + public fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + public fun build(): RelocatePathContext = RelocatePathContext(path, stats) } - companion object { + public companion object { @JvmStatic - fun builder(): Builder = Builder() + public fun builder(): Builder = Builder() } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt index 03fea7f5d..486877e7b 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -6,18 +6,18 @@ package com.github.jengelman.gradle.plugins.shadow.relocation * @author Jason van Zyl * @author John Engelman */ -interface Relocator { - fun canRelocatePath(path: String): Boolean +public interface Relocator { + public fun canRelocatePath(path: String): Boolean - fun relocatePath(context: RelocatePathContext): String + public fun relocatePath(context: RelocatePathContext): String - fun canRelocateClass(className: String): Boolean + public fun canRelocateClass(className: String): Boolean - fun relocateClass(context: RelocateClassContext): String + public fun relocateClass(context: RelocateClassContext): String - fun applyToSourceContent(sourceContent: String): String + public fun applyToSourceContent(sourceContent: String): String - companion object { - val ROLE: String = Relocator::class.java.name + public companion object { + public val ROLE: String = Relocator::class.java.name } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 98feac8f3..7e34f2e1e 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -13,7 +13,7 @@ import org.gradle.api.tasks.Optional * @author John Engelman */ @CacheableRelocator -open class SimpleRelocator @JvmOverloads constructor( +public open class SimpleRelocator @JvmOverloads constructor( pattern: String?, shadedPattern: String?, includes: List?, @@ -55,32 +55,32 @@ open class SimpleRelocator @JvmOverloads constructor( @get:Input @get:Optional - open val pattern: String? get() = _pattern + public open val pattern: String? get() = _pattern @get:Input - open val pathPattern: String get() = _pathPattern + public open val pathPattern: String get() = _pathPattern @get:Input @get:Optional - open val shadedPattern: String? get() = _shadedPattern + public open val shadedPattern: String? get() = _shadedPattern @get:Input - open val shadedPathPattern: String get() = _shadedPathPattern + public open val shadedPathPattern: String get() = _shadedPathPattern @get:Input - open val isRawString: Boolean get() = _isRawString + public open val isRawString: Boolean get() = _isRawString @get:Input - open val includes: Set get() = _includes + public open val includes: Set get() = _includes @get:Input - open val excludes: Set get() = _excludes + public open val excludes: Set get() = _excludes - open fun include(pattern: String): SimpleRelocator = apply { + public open fun include(pattern: String): SimpleRelocator = apply { _includes += normalizePatterns(listOf(pattern)) } - open fun exclude(pattern: String): SimpleRelocator = apply { + public open fun exclude(pattern: String): SimpleRelocator = apply { _excludes += normalizePatterns(listOf(pattern)) } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt index 20f9d7ff6..bb33441bb 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt @@ -6,7 +6,7 @@ import org.gradle.api.java.archives.Manifest import org.gradle.api.java.archives.internal.DefaultManifest import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec -open class DefaultInheritManifest @JvmOverloads constructor( +public open class DefaultInheritManifest @JvmOverloads constructor( private val fileResolver: FileResolver, private val internalManifest: DefaultManifest = DefaultManifest(fileResolver), ) : InheritManifest, diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt index 13c3a3ab3..d370b5928 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt @@ -3,8 +3,8 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import org.gradle.api.Action import org.gradle.api.java.archives.Manifest -interface InheritManifest : Manifest { - fun inheritFrom(vararg inheritPaths: Any): InheritManifest +public interface InheritManifest : Manifest { + public fun inheritFrom(vararg inheritPaths: Any): InheritManifest - fun inheritFrom(vararg inheritPaths: Any, action: Action<*>?): InheritManifest + public fun inheritFrom(vararg inheritPaths: Any, action: Action<*>?): InheritManifest } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt index c7531ca6d..fdffe3788 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt @@ -4,21 +4,21 @@ import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import org.gradle.api.DefaultTask import org.gradle.api.tasks.TaskAction -abstract class KnowsTask : DefaultTask() { +public abstract class KnowsTask : DefaultTask() { @TaskAction - fun knows() { + public fun knows() { logger.info( """ - No, The Shadow Knows.... + No, The Shadow Knows.... - ${this::class.java.requireResourceAsText("/shadowBanner.txt")} + ${this::class.java.requireResourceAsText("/shadowBanner.txt")} """.trimIndent(), ) } - companion object { - const val NAME: String = "knows" - const val DESC: String = "Do you know who knows?" + public companion object { + public const val NAME: String = "knows" + public const val DESC: String = "Do you know who knows?" } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 5149de444..030538f0b 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -38,7 +38,7 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.commons.ClassRemapper import org.slf4j.LoggerFactory -open class ShadowCopyAction internal constructor( +public open class ShadowCopyAction internal constructor( private val zipFile: File, private val compressor: ZipCompressor, private val documentationRegistry: DocumentationRegistry, @@ -52,7 +52,7 @@ open class ShadowCopyAction internal constructor( private val unusedTracker: UnusedTracker?, ) : CopyAction { - constructor( + public constructor( zipFile: File, compressor: ZipCompressor, documentationRegistry: DocumentationRegistry, @@ -144,7 +144,7 @@ open class ShadowCopyAction internal constructor( return zipEntry } - abstract class BaseStreamAction : CopyActionProcessingStreamAction { + public abstract class BaseStreamAction : CopyActionProcessingStreamAction { protected fun isArchive(fileDetails: FileCopyDetails): Boolean { return fileDetails.relativePath.pathString.endsWith(".jar") } @@ -384,14 +384,14 @@ open class ShadowCopyAction internal constructor( } } - open inner class RelativeArchivePath( - open val entry: ZipEntry, + public open inner class RelativeArchivePath( + public open val entry: ZipEntry, ) : RelativePath( !entry.isDirectory, // `dir/` will be split into ["dir", ""], we have to trim empty segments here. *entry.name.split('/').filter(CharSequence::isNotEmpty).toTypedArray(), ) { - open val isClassFile: Boolean get() = lastName.endsWith(".class") + public open val isClassFile: Boolean get() = lastName.endsWith(".class") override fun getParent(): RelativeArchivePath? { return if (segments.size <= 1) { @@ -405,10 +405,10 @@ open class ShadowCopyAction internal constructor( } } - open class ArchiveFileTreeElement( + public open class ArchiveFileTreeElement( private val archivePath: RelativeArchivePath, ) : FileTreeElement { - open val isClassFile: Boolean get() = archivePath.isClassFile + public open val isClassFile: Boolean get() = archivePath.isClassFile override fun isDirectory(): Boolean = archivePath.entry.isDirectory @@ -427,7 +427,7 @@ open class ShadowCopyAction internal constructor( override fun getPermissions(): FilePermissions = DefaultFilePermissions(mode) - open fun asFileTreeElement(): FileTreeElement { + public open fun asFileTreeElement(): FileTreeElement { return DefaultFileTreeElement(null, RelativePath(!isDirectory, *archivePath.segments), null, null) } @@ -440,8 +440,8 @@ open class ShadowCopyAction internal constructor( override fun copyTo(file: File): Boolean = throw UnsupportedOperationException() } - companion object { + public companion object { private val logger = LoggerFactory.getLogger(ShadowCopyAction::class.java) - val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis + public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index bdb9806f0..bd8fdcc84 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -41,7 +41,7 @@ import org.gradle.api.tasks.bundling.ZipEntryCompression import org.gradle.api.tasks.util.PatternSet @CacheableTask -abstract class ShadowJar : +public abstract class ShadowJar : Jar(), ShadowSpec { private val _transformers = mutableListOf() @@ -78,7 +78,7 @@ abstract class ShadowJar : override val stats: ShadowStats get() = _stats @get:Classpath - val toMinimize: FileCollection + public val toMinimize: FileCollection get() { if (_toMinimize == null) { _toMinimize = if (minimizeJar) { @@ -92,7 +92,7 @@ abstract class ShadowJar : } @get:Classpath - val apiJars: FileCollection + public val apiJars: FileCollection get() { if (_apiJars == null) { _apiJars = if (minimizeJar) { @@ -106,7 +106,7 @@ abstract class ShadowJar : @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) - val sourceSetsClassesDirs: FileCollection + public val sourceSetsClassesDirs: FileCollection get() { if (_sourceSetsClassesDirs == null) { val allClassesDirs = project.objects.fileCollection() @@ -121,16 +121,16 @@ abstract class ShadowJar : } @get:Classpath - val includedDependencies: FileCollection get() = _includedDependencies + public val includedDependencies: FileCollection get() = _includedDependencies @get:Internal - val rootPatternSet: PatternSet + public val rootPatternSet: PatternSet get() { return (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet } @get:Internal - val internalCompressor: ZipCompressor + public val internalCompressor: ZipCompressor get() { return when (entryCompression) { ZipEntryCompression.DEFLATED -> DefaultZipCompressor(isZip64, ZipOutputStream.DEFLATED) @@ -140,7 +140,7 @@ abstract class ShadowJar : } @get:Nested - var transformers: List + public var transformers: List get() = _transformers set(value) { _transformers.clear() @@ -148,7 +148,7 @@ abstract class ShadowJar : } @get:Nested - var relocators: List + public var relocators: List get() = _relocators set(value) { _relocators.clear() @@ -157,7 +157,7 @@ abstract class ShadowJar : @get:Classpath @get:Optional - var configurations: List + public var configurations: List get() = _configurations set(value) { _configurations.clear() @@ -165,21 +165,21 @@ abstract class ShadowJar : } @get:Internal - var dependencyFilter: DependencyFilter + public var dependencyFilter: DependencyFilter get() = _dependencyFilter set(value) { _dependencyFilter = value } @get:Input - var isEnableRelocation: Boolean + public var isEnableRelocation: Boolean get() = _isEnableRelocation set(value) { _isEnableRelocation = value } @get:Input - var relocationPrefix: String + public var relocationPrefix: String get() = _relocationPrefix set(value) { _relocationPrefix = value diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt index 826d201ba..f51bf1716 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -10,12 +10,12 @@ import java.lang.reflect.InvocationTargetException import org.gradle.api.Action import org.gradle.api.file.CopySpec -interface ShadowSpec : CopySpec { - fun minimize(): ShadowSpec +public interface ShadowSpec : CopySpec { + public fun minimize(): ShadowSpec - fun minimize(action: Action?): ShadowSpec + public fun minimize(action: Action?): ShadowSpec - fun dependencies(action: Action?): ShadowSpec + public fun dependencies(action: Action?): ShadowSpec @Throws( InstantiationException::class, @@ -23,7 +23,7 @@ interface ShadowSpec : CopySpec { NoSuchMethodException::class, InvocationTargetException::class, ) - fun transform(clazz: Class): ShadowSpec + public fun transform(clazz: Class): ShadowSpec @Throws( InstantiationException::class, @@ -31,25 +31,25 @@ interface ShadowSpec : CopySpec { NoSuchMethodException::class, InvocationTargetException::class, ) - fun transform(clazz: Class, action: Action?): ShadowSpec + public fun transform(clazz: Class, action: Action?): ShadowSpec - fun transform(transformer: Transformer): ShadowSpec + public fun transform(transformer: Transformer): ShadowSpec - fun mergeServiceFiles(): ShadowSpec + public fun mergeServiceFiles(): ShadowSpec - fun mergeServiceFiles(rootPath: String): ShadowSpec + public fun mergeServiceFiles(rootPath: String): ShadowSpec - fun mergeServiceFiles(action: Action?): ShadowSpec + public fun mergeServiceFiles(action: Action?): ShadowSpec - fun mergeGroovyExtensionModules(): ShadowSpec + public fun mergeGroovyExtensionModules(): ShadowSpec - fun append(resourcePath: String): ShadowSpec + public fun append(resourcePath: String): ShadowSpec - fun relocate(pattern: String, destination: String): ShadowSpec + public fun relocate(pattern: String, destination: String): ShadowSpec - fun relocate(pattern: String, destination: String, action: Action?): ShadowSpec + public fun relocate(pattern: String, destination: String, action: Action?): ShadowSpec - fun relocate(relocator: Relocator): ShadowSpec + public fun relocate(relocator: Relocator): ShadowSpec @Throws( InstantiationException::class, @@ -57,7 +57,7 @@ interface ShadowSpec : CopySpec { NoSuchMethodException::class, InvocationTargetException::class, ) - fun relocate(clazz: Class): ShadowSpec + public fun relocate(clazz: Class): ShadowSpec @Throws( InstantiationException::class, @@ -65,7 +65,7 @@ interface ShadowSpec : CopySpec { NoSuchMethodException::class, InvocationTargetException::class, ) - fun relocate(clazz: Class, action: Action?): ShadowSpec + public fun relocate(clazz: Class, action: Action?): ShadowSpec - val stats: ShadowStats + public val stats: ShadowStats } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt index a032298de..3cfc90c5f 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt @@ -2,4 +2,4 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory -interface ZipCompressor : ArchiveOutputStreamFactory +public interface ZipCompressor : ArchiveOutputStreamFactory diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index 132c9efa3..e58350ae5 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -9,7 +9,7 @@ import org.gradle.api.file.FileTreeElement * * @author John Engelman */ -open class ApacheLicenseResourceTransformer : Transformer by NoOpTransformer { +public open class ApacheLicenseResourceTransformer : Transformer by NoOpTransformer { override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString return LICENSE_PATH.equals(path, ignoreCase = true) || diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 5f1c3347f..474d99bf2 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -18,7 +18,7 @@ import org.gradle.api.tasks.Optional * * @author John Engelman */ -open class ApacheNoticeResourceTransformer : Transformer { +public open class ApacheNoticeResourceTransformer : Transformer { private val entries = mutableSetOf() private val organizationEntries = mutableMapOf>() private val charset get() = if (encoding.isNullOrEmpty()) Charsets.UTF_8 else Charset.forName(encoding) @@ -27,43 +27,43 @@ open class ApacheNoticeResourceTransformer : Transformer { * MSHADE-101 :: NullPointerException when projectName is missing */ @get:Input - var projectName: String = "" + public var projectName: String = "" @get:Input - var addHeader: Boolean = true + public var addHeader: Boolean = true @get:Input - var preamble1: String = """ + public var preamble1: String = """ // ------------------------------------------------------------------ // NOTICE file corresponding to the section 4d of The Apache License, // Version 2.0, in this case for """.trimIndent() @get:Input - var preamble2: String = "\n// ------------------------------------------------------------------\n" + public var preamble2: String = "\n// ------------------------------------------------------------------\n" @get:Input - var preamble3: String = "This product includes software developed at\n" + public var preamble3: String = "This product includes software developed at\n" @get:Input - var organizationName: String = "The Apache Software Foundation" + public var organizationName: String = "The Apache Software Foundation" @get:Input - var organizationURL: String = "http://www.apache.org/" + public var organizationURL: String = "http://www.apache.org/" @get:Input - var inceptionYear: String = "2006" + public var inceptionYear: String = "2006" @get:Optional @get:Input - var copyright: String? = null + public var copyright: String? = null /** * The file encoding of the `NOTICE` file. */ @get:Optional @get:Input - var encoding: String? = null + public var encoding: String? = null override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index 3cc27f60a..fcaa2d5a7 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -17,7 +17,7 @@ import org.gradle.api.tasks.Optional */ @CacheableTransformer @Suppress("ktlint:standard:backing-property-naming") -open class AppendingTransformer : Transformer { +public open class AppendingTransformer : Transformer { /** * Defer initialization, see [issue 763](https://github.com/GradleUp/shadow/issues/763). */ @@ -26,7 +26,7 @@ open class AppendingTransformer : Transformer { @get:Optional @get:Input - var resource: String? = null + public var resource: String? = null override fun canTransformResource(element: FileTreeElement): Boolean { return resource.equals(element.relativePath.pathString, ignoreCase = true) diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt index 6bea5011c..a240b27e9 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt @@ -7,4 +7,4 @@ package com.github.jengelman.gradle.plugins.shadow.transformers */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS) -annotation class CacheableTransformer +public annotation class CacheableTransformer diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index 8616a2605..b917d47b8 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -21,7 +21,7 @@ import org.gradle.api.file.FileTreeElement * * @author John Engelman */ -open class ComponentsXmlResourceTransformer : Transformer { +public open class ComponentsXmlResourceTransformer : Transformer { private val components = mutableMapOf() override fun canTransformResource(element: FileTreeElement): Boolean { @@ -105,8 +105,8 @@ open class ComponentsXmlResourceTransformer : Transformer { return os.toByteArray() } - companion object { - const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" + public companion object { + public const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" private fun getRelocatedClass(className: String?, context: TransformerContext): String? { val stats = context.stats diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index 15ac00db7..bc3ee2346 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -11,10 +11,10 @@ import org.gradle.api.tasks.Optional * * @author John Engelman */ -open class DontIncludeResourceTransformer : Transformer by NoOpTransformer { +public open class DontIncludeResourceTransformer : Transformer by NoOpTransformer { @get:Optional @get:Input - var resource: String? = null + public var resource: String? = null override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index e5bbce6d6..36d3258c3 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -25,7 +25,7 @@ import org.gradle.api.file.FileTreeElement * Note that certain JDK9+ tooling will break when using the legacy location. */ @CacheableTransformer -open class GroovyExtensionModuleTransformer : Transformer { +public open class GroovyExtensionModuleTransformer : Transformer { private val module = Properties() /** diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index fa69cf8d4..3d5315ef3 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -17,13 +17,13 @@ import org.gradle.api.tasks.PathSensitivity * * @author John Engelman */ -open class IncludeResourceTransformer : Transformer by NoOpTransformer { +public open class IncludeResourceTransformer : Transformer by NoOpTransformer { @get:InputFile @get:PathSensitive(PathSensitivity.NONE) - var file: File? = null + public var file: File? = null @get:Input - var resource: String? = null + public var resource: String? = null override fun hasTransformedResource(): Boolean = file?.exists() == true diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index 53e3cbf19..5fcb1d25d 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -24,7 +24,7 @@ import org.gradle.api.file.FileTreeElement * @see [PluginsCacheFileTransformer.java](https://github.com/edwgiz/maven-shaded-log4j-transformer/blob/master/src/main/java/com/github/edwgiz/mavenShadePlugin/log4j2CacheTransformer/PluginsCacheFileTransformer.java) */ @CacheableTransformer -open class Log4j2PluginsCacheFileTransformer : Transformer { +public open class Log4j2PluginsCacheFileTransformer : Transformer { private val temporaryFiles = mutableListOf() private val relocators = mutableListOf() private var stats: ShadowStats? = null diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 6e1965ba5..80f306343 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -17,12 +17,12 @@ import org.slf4j.LoggerFactory * Modified from [ManifestResourceTransformer]. * @author Chris Rankin */ -open class ManifestAppenderTransformer : Transformer { +public open class ManifestAppenderTransformer : Transformer { private var manifestContents = ByteArray(0) private val _attributes = mutableListOf>>() @get:Input - open val attributes: List>> get() = _attributes + public open val attributes: List>> get() = _attributes override fun canTransformResource(element: FileTreeElement): Boolean { return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) @@ -62,7 +62,7 @@ open class ManifestAppenderTransformer : Transformer { } } - open fun append(name: String, value: Comparable<*>): ManifestAppenderTransformer = apply { + public open fun append(name: String, value: Comparable<*>): ManifestAppenderTransformer = apply { _attributes.add(Pair(name, value)) } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 900e4cb98..c65d0bf2a 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -22,17 +22,17 @@ import org.slf4j.LoggerFactory * @author Jason van Zyl * @author John Engelman */ -open class ManifestResourceTransformer : Transformer { +public open class ManifestResourceTransformer : Transformer { private var manifestDiscovered = false private var manifest: Manifest? = null @get:Optional @get:Input - var mainClass: String? = null + public var mainClass: String? = null @get:Optional @get:Input - var manifestEntries: MutableMap? = null + public var manifestEntries: MutableMap? = null override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString @@ -75,7 +75,7 @@ open class ManifestResourceTransformer : Transformer { manifest!!.write(os) } - open fun attributes(attributes: Map): ManifestResourceTransformer = apply { + public open fun attributes(attributes: Map): ManifestResourceTransformer = apply { if (manifestEntries == null) { manifestEntries = LinkedHashMap() } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 2501f04f0..48844066f 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -89,33 +89,33 @@ import org.gradle.api.tasks.Internal * @author Andres Almiray * @author Marc Philipp */ -open class PropertiesFileTransformer : Transformer { +public open class PropertiesFileTransformer : Transformer { private val propertiesEntries = mutableMapOf() private val _charset get() = Charset.forName(charset) @get:Input - var paths: List = listOf() + public var paths: List = listOf() @get:Input - var mappings: Map> = mapOf() + public var mappings: Map> = mapOf() /** * Optional values: first, latest, append. */ @get:Input - var mergeStrategy: String = "first" + public var mergeStrategy: String = "first" @get:Input - var mergeSeparator: String = "," + public var mergeSeparator: String = "," @get:Input - var charset: String = "ISO_8859_1" + public var charset: String = "ISO_8859_1" /** * Use [java.util.function.Function] here for compatibility with Groovy and Java. */ @get:Internal - var keyTransformer: Function = IDENTITY + public var keyTransformer: Function = IDENTITY override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index d2f4e37db..112e5e734 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -28,7 +28,7 @@ import org.gradle.api.tasks.util.PatternSet * @author John Engelman */ @CacheableTransformer -open class ServiceFileTransformer( +public open class ServiceFileTransformer( private val patternSet: PatternSet = PatternSet() .include(SERVICES_PATTERN) .exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN), @@ -89,13 +89,13 @@ open class ServiceFileTransformer( @Input override fun getExcludes(): Set = patternSet.excludes - open fun setPath(path: String): PatternFilterable = apply { + public open fun setPath(path: String): PatternFilterable = apply { patternSet.setIncludes(listOf("$path/**")) } - open class ServiceStream : ByteArrayOutputStream(1024) { + public open class ServiceStream : ByteArrayOutputStream(1024) { @Throws(IOException::class) - open fun append(inputStream: InputStream) { + public open fun append(inputStream: InputStream) { if (count > 0 && buf[count - 1] != '\n'.code.toByte() && buf[count - 1] != '\r'.code.toByte()) { val newline = "\n".toByteArray() write(newline, 0, newline.size) @@ -105,7 +105,7 @@ open class ServiceFileTransformer( } } - open fun toInputStream(): InputStream = ByteArrayInputStream(buf, 0, count) + public open fun toInputStream(): InputStream = ByteArrayInputStream(buf, 0, count) } private companion object { diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index 6039d8db4..59056025f 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -12,22 +12,22 @@ import org.gradle.api.tasks.Internal * @author Charlie Knudsen * @author John Engelman */ -interface Transformer : Named { - fun canTransformResource(element: FileTreeElement): Boolean +public interface Transformer : Named { + public fun canTransformResource(element: FileTreeElement): Boolean - fun transform(context: TransformerContext) + public fun transform(context: TransformerContext) - fun hasTransformedResource(): Boolean + public fun hasTransformedResource(): Boolean - fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) + public fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) @Internal override fun getName(): String = this::class.java.simpleName } -object NoOpTransformer : Transformer { - override fun canTransformResource(element: FileTreeElement): Boolean = false - override fun transform(context: TransformerContext): Unit = Unit - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean): Unit = Unit - override fun hasTransformedResource(): Boolean = false +public object NoOpTransformer : Transformer { + public override fun canTransformResource(element: FileTreeElement): Boolean = false + public override fun transform(context: TransformerContext): Unit = Unit + public override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean): Unit = Unit + public override fun hasTransformedResource(): Boolean = false } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt index 00a7801f9..3bb9b1351 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -5,23 +5,23 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import java.io.InputStream -data class TransformerContext @JvmOverloads constructor( +public data class TransformerContext @JvmOverloads constructor( val path: String, val inputStream: InputStream, val relocators: List = emptyList(), val stats: ShadowStats = ShadowStats(), ) { - class Builder { + public class Builder { private var path = "" private var inputStream: InputStream? = null private var relocators = emptyList() private var stats = ShadowStats() - fun path(path: String): Builder = apply { this.path = path } - fun inputStream(inputStream: InputStream): Builder = apply { this.inputStream = inputStream } - fun relocators(relocators: List): Builder = apply { this.relocators = relocators } - fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } - fun build(): TransformerContext = TransformerContext( + public fun path(path: String): Builder = apply { this.path = path } + public fun inputStream(inputStream: InputStream): Builder = apply { this.inputStream = inputStream } + public fun relocators(relocators: List): Builder = apply { this.relocators = relocators } + public fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } + public fun build(): TransformerContext = TransformerContext( path = path, inputStream = inputStream ?: error("inputStream is required"), relocators = relocators, @@ -29,12 +29,12 @@ data class TransformerContext @JvmOverloads constructor( ) } - companion object { + public companion object { @JvmStatic - fun builder(): Builder = Builder() + public fun builder(): Builder = Builder() @JvmStatic - fun getEntryTimestamp(preserveFileTimestamps: Boolean, entryTime: Long): Long { + public fun getEntryTimestamp(preserveFileTimestamps: Boolean, entryTime: Long): Long { return if (preserveFileTimestamps) entryTime else ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES } } diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index b0d74d03b..e9fc642df 100644 --- a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -23,15 +23,15 @@ import org.xml.sax.InputSource * @author John Engelman */ @CacheableTransformer -open class XmlAppendingTransformer : Transformer { +public open class XmlAppendingTransformer : Transformer { private var doc: Document? = null @get:Input - var ignoreDtd: Boolean = true + public var ignoreDtd: Boolean = true @get:Optional @get:Input - var resource: String? = null + public var resource: String? = null override fun canTransformResource(element: FileTreeElement): Boolean { return resource?.equals(element.relativePath.pathString, ignoreCase = true) == true @@ -76,7 +76,7 @@ open class XmlAppendingTransformer : Transformer { doc = null } - companion object { - const val XSI_NS: String = "http://www.w3.org/2001/XMLSchema-instance" + public companion object { + public const val XSI_NS: String = "http://www.w3.org/2001/XMLSchema-instance" } } From 976f5893b4922a34b910146c92f8bfd3fa2b2877 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 10:33:32 -0500 Subject: [PATCH 016/941] Finish Kotlin migration (#1012) This reverts parts of 02f40fdd --- .editorconfig | 3 -- api/build.gradle.kts | 48 ------------------- build-logic/build.gradle.kts | 1 + .../shadow.convention.deploy.gradle.kts | 4 +- .../shadow.convention.publish.gradle.kts | 11 +---- build.gradle.kts | 10 ++-- settings.gradle.kts | 3 -- src/docs/changes/README.md | 4 ++ .../plugins/shadow/ShadowApplicationPlugin.kt | 0 .../gradle/plugins/shadow/ShadowBasePlugin.kt | 0 .../gradle/plugins/shadow/ShadowExtension.kt | 0 .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 0 .../gradle/plugins/shadow/ShadowPlugin.kt | 0 .../gradle/plugins/shadow/ShadowStats.kt | 0 .../plugins/shadow/impl/RelocatorRemapper.kt | 0 .../internal/AbstractDependencyFilter.kt | 0 .../shadow/internal/CleanProperties.kt | 0 .../internal/DefaultDependencyFilter.kt | 0 .../shadow/internal/DefaultZipCompressor.kt | 0 .../shadow/internal/DependencyFilter.kt | 0 .../plugins/shadow/internal/JavaJarExec.kt | 0 .../internal/MinimizeDependencyFilter.kt | 0 .../plugins/shadow/internal/UnusedTracker.kt | 0 .../gradle/plugins/shadow/internal/Utils.kt | 0 .../shadow/legacy/LegacyShadowPlugin.kt | 0 .../shadow/relocation/CacheableRelocator.kt | 0 .../shadow/relocation/RelocateClassContext.kt | 0 .../shadow/relocation/RelocatePathContext.kt | 0 .../plugins/shadow/relocation/Relocator.kt | 0 .../shadow/relocation/SimpleRelocator.kt | 0 .../shadow/tasks/DefaultInheritManifest.kt | 0 .../plugins/shadow/tasks/InheritManifest.kt | 0 .../gradle/plugins/shadow/tasks/KnowsTask.kt | 0 .../plugins/shadow/tasks/ShadowCopyAction.kt | 0 .../gradle/plugins/shadow/tasks/ShadowJar.kt | 0 .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 0 .../plugins/shadow/tasks/ZipCompressor.kt | 0 .../ApacheLicenseResourceTransformer.kt | 0 .../ApacheNoticeResourceTransformer.kt | 0 .../transformers/AppendingTransformer.kt | 0 .../transformers/CacheableTransformer.kt | 0 .../ComponentsXmlResourceTransformer.kt | 0 .../DontIncludeResourceTransformer.kt | 0 .../GroovyExtensionModuleTransformer.kt | 0 .../IncludeResourceTransformer.kt | 0 .../Log4j2PluginsCacheFileTransformer.kt | 0 .../ManifestAppenderTransformer.kt | 0 .../ManifestResourceTransformer.kt | 0 .../transformers/PropertiesFileTransformer.kt | 0 .../transformers/ServiceFileTransformer.kt | 0 .../shadow/transformers/Transformer.kt | 0 .../shadow/transformers/TransformerContext.kt | 0 .../transformers/XmlAppendingTransformer.kt | 0 .../plugins/shadow/ShadowPluginSpec.groovy | 8 +--- .../shadow/util/PluginSpecification.groovy | 3 +- 55 files changed, 17 insertions(+), 78 deletions(-) delete mode 100644 api/build.gradle.kts rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt (100%) rename {api/src => src}/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt (100%) diff --git a/.editorconfig b/.editorconfig index 97c1b8453..f36ddfdde 100755 --- a/.editorconfig +++ b/.editorconfig @@ -7,9 +7,6 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[*.{groovy,java}] -indent_size = 4 - [*.{kt,kts}] ij_kotlin_imports_layout = * ij_kotlin_allow_trailing_comma = true diff --git a/api/build.gradle.kts b/api/build.gradle.kts deleted file mode 100644 index 06d3b7a96..000000000 --- a/api/build.gradle.kts +++ /dev/null @@ -1,48 +0,0 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion - -plugins { - kotlin("jvm") - `java-gradle-plugin` - id("com.diffplug.spotless") -} - -java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 -} - -kotlin { - explicitApi() - compilerOptions { - // https://docs.gradle.org/current/userguide/compatibility.html#kotlin - apiVersion = KotlinVersion.KOTLIN_1_8 - jvmTarget = JvmTarget.JVM_1_8 - freeCompilerArgs.addAll( - "-Xjvm-default=all", - ) - } -} - -spotless { - kotlin { - ktlint().editorConfigOverride( - mapOf( - "indent_size" to "2", - ), - ) - } -} - -dependencies { - compileOnly("com.gradleup.shadow:shadow-gradle-plugin:8.3.5") - - implementation("org.jdom:jdom2:2.0.6.1") - implementation("org.ow2.asm:asm-commons:9.7.1") - implementation("commons-io:commons-io:2.18.0") - implementation("org.apache.ant:ant:1.10.15") - implementation("org.codehaus.plexus:plexus-utils:4.0.2") - implementation("org.codehaus.plexus:plexus-xml:4.0.4") - implementation("org.apache.logging.log4j:log4j-core:2.24.1") - implementation("org.vafer:jdependency:2.11") -} diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 1622a3797..00ca0a991 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -10,6 +10,7 @@ repositories { dependencies { implementation("com.gradle.publish:plugin-publish-plugin:1.3.0") implementation("com.vanniktech:gradle-maven-publish-plugin:0.30.0") + implementation("org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin:1.9.20") implementation("org.ajoberstar.git-publish:gradle-git-publish:4.2.2") implementation("com.github.node-gradle:gradle-node-plugin:7.1.0") } diff --git a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts index 98c7b1ebe..39f4dd10f 100644 --- a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts @@ -11,7 +11,7 @@ gitPublish { contents { from("build/site") into("api") { - from(tasks.named("groovydoc")) + from(tasks.named("dokkaHtml")) } filter( "tokens" to mapOf( @@ -33,5 +33,5 @@ val yarnBuild = tasks.named("yarn_build") { } tasks.gitPublishCopy { - dependsOn(yarnBuild, tasks.named("groovydoc")) + dependsOn(yarnBuild, tasks.named("dokkaHtml")) } diff --git a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts index 6282e8c30..750dda2c2 100644 --- a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts @@ -1,6 +1,7 @@ plugins { id("com.gradle.plugin-publish") id("com.vanniktech.maven.publish") + id("org.jetbrains.dokka") } version = providers.gradleProperty("VERSION_NAME").get() @@ -36,16 +37,6 @@ tasks.publishPlugins { notCompatibleWithConfigurationCache("https://github.com/gradle/gradle/issues/21283") } -tasks.withType().configureEach { - (options as? StandardJavadocDocletOptions)?.let { - it.links( - "https://docs.oracle.com/en/java/javase/17/docs/api/", - "https://docs.groovy-lang.org/2.4.7/html/gapi/", - ) - it.addStringOption("Xdoclint:none", "-quiet") - } -} - configurations { listOf( apiElements, diff --git a/build.gradle.kts b/build.gradle.kts index 65de1e4aa..ec16c0a78 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,8 +3,7 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion plugins { kotlin("jvm") version "2.0.21" - groovy - `java-gradle-plugin` + groovy // Required for Spock tests. id("shadow.convention.publish") id("shadow.convention.deploy") id("com.diffplug.spotless") version "7.0.0.BETA4" @@ -16,6 +15,7 @@ java { } kotlin { + explicitApi() compilerOptions { // https://docs.gradle.org/current/userguide/compatibility.html#kotlin apiVersion = KotlinVersion.KOTLIN_1_8 @@ -27,6 +27,9 @@ kotlin { } spotless { + kotlin { + ktlint() + } kotlinGradle { ktlint() target("**/*.kts") @@ -35,9 +38,6 @@ spotless { } dependencies { - compileOnly(localGroovy()) - implementation(projects.api) - implementation("org.jdom:jdom2:2.0.6.1") implementation("org.ow2.asm:asm-commons:9.7.1") implementation("commons-io:commons-io:2.18.0") diff --git a/settings.gradle.kts b/settings.gradle.kts index a76e1195b..87569ed1f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -30,6 +30,3 @@ dependencyResolutionManagement { rootProject.name = "shadow" enableFeaturePreview("STABLE_CONFIGURATION_CACHE") -enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") - -include(":api") diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index d1d7af7c7..bdc8c1d01 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Changed** + +- **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) + **Removed** - **BREAKING CHANGE:** Remove Develocity integration. ([#1013](https://github.com/GradleUp/shadow/pull/1013)) diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt diff --git a/api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt similarity index 100% rename from api/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index a19284e5a..8a9f640b6 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -82,8 +82,6 @@ class ShadowPluginSpec extends PluginSpecification { when: run(['shadowJar']) { it.withGradleVersion(version) - it.withDebug(true) - it.withTestKitDir(getTestKitDir()) } then: @@ -108,8 +106,6 @@ class ShadowPluginSpec extends PluginSpecification { expect: runWithFailure(['shadowJar']) { it.withGradleVersion('7.0') - it.withDebug(true) - it.withTestKitDir(getTestKitDir()) } } @@ -387,7 +383,7 @@ class ShadowPluginSpec extends PluginSpecification { package client; import junit.framework.TestCase; public class Client extends TestCase { - public static void main(String[] args) {} + public static void main(String[] args) {} } """.stripIndent() @@ -1029,7 +1025,7 @@ class ShadowPluginSpec extends PluginSpecification { version = '1.0' repositories { maven { url "${repo.uri}" } } dependencies { api project(':api') } - + shadowJar.minimize() """.stripIndent() diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy index d33e95530..d754c9acf 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy @@ -58,6 +58,7 @@ abstract class PluginSpecification extends Specification { .withProjectDir(dir.toFile()) .forwardOutput() .withPluginClasspath() + .withTestKitDir(testKitDir) } GradleRunner runner(Collection tasks) { @@ -177,7 +178,7 @@ abstract class PluginSpecification extends Specification { return new File(this.class.classLoader.getResource(name).toURI()) } - static File getTestKitDir() { + protected static File getTestKitDir() { def gradleUserHome = System.getenv("GRADLE_USER_HOME") if (!gradleUserHome) { gradleUserHome = new File(System.getProperty("user.home"), ".gradle").absolutePath From b385f289b184bb367083ec787b46cfb04da084fe Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 10:46:04 -0500 Subject: [PATCH 017/941] Set up BCV (#1032) --- api/shadow.api | 610 +++++++++++++++++++++++++++++++++++++++++++++++ build.gradle.kts | 1 + 2 files changed, 611 insertions(+) create mode 100644 api/shadow.api diff --git a/api/shadow.api b/api/shadow.api new file mode 100644 index 000000000..c847f756e --- /dev/null +++ b/api/shadow.api @@ -0,0 +1,610 @@ +public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin : org/gradle/api/Plugin { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion; + public static final field SHADOW_INSTALL_TASK_NAME Ljava/lang/String; + public static final field SHADOW_RUN_TASK_NAME Ljava/lang/String; + public static final field SHADOW_SCRIPTS_TASK_NAME Ljava/lang/String; + public fun ()V + protected fun addCreateScriptsTask ()V + protected fun addRunTask ()V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V + protected fun configureDistSpec ()V + protected fun configureInstallTask ()V + protected fun configureJarMainClass ()V + protected final fun getShadowJar ()Lorg/gradle/api/tasks/TaskProvider; +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion { +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin : org/gradle/api/Plugin { + public static final field COMPONENT_NAME Ljava/lang/String; + public static final field CONFIGURATION_NAME Ljava/lang/String; + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$Companion; + public static final field DISTRIBUTION_NAME Ljava/lang/String; + public static final field EXTENSION_NAME Ljava/lang/String; + public static final field GROUP_NAME Ljava/lang/String; + public static final field SHADOW Ljava/lang/String; + public fun ()V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$Companion { +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { + public fun (Lorg/gradle/api/Project;)V + public final fun component (Lorg/gradle/api/publish/maven/MavenPublication;)V +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin : org/gradle/api/Plugin { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion; + public static final field SHADOW_JAR_TASK_NAME Ljava/lang/String; + public static final field SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME Ljava/lang/String; + public fun (Lorg/gradle/api/component/SoftwareComponentFactory;)V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion { +} + +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 + public fun apply (Lorg/gradle/api/Project;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/ShadowStats { + public fun ()V + public fun finishJar ()V + public fun getAverageTimePerJar ()D + public fun getAverageTimeSecsPerJar ()D + public fun getBuildScanData ()Ljava/util/Map; + public fun getJarCount ()I + public fun getJarEndTime ()J + public fun getJarStartTime ()J + public fun getJarTiming ()J + public fun getProcessingJar ()Z + public fun getRelocationString ()Ljava/lang/String; + public fun getRelocations ()Ljava/util/Map; + public fun getTotalTime ()J + public fun getTotalTimeSecs ()D + public fun printStats ()V + public fun relocate (Ljava/lang/String;Ljava/lang/String;)V + public fun setJarCount (I)V + public fun setJarEndTime (J)V + public fun setJarStartTime (J)V + public fun setProcessingJar (Z)V + public fun setTotalTime (J)V + public fun startJar ()V + public fun toString ()Ljava/lang/String; +} + +public class com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper : org/objectweb/asm/commons/Remapper { + public fun (Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public fun hasRelocators ()Z + public fun map (Ljava/lang/String;)Ljava/lang/String; + public fun mapPath (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath;)Ljava/lang/String; + public fun mapPath (Ljava/lang/String;)Ljava/lang/String; + public fun mapValue (Ljava/lang/Object;)Ljava/lang/Object; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter { + public abstract fun dependency (Lgroovy/lang/Closure;)Lorg/gradle/api/specs/Spec; + public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; + public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; + public abstract fun exclude (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter; + public abstract fun include (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter; + public abstract fun project (Ljava/lang/String;)Lorg/gradle/api/specs/Spec; + public abstract fun project (Ljava/util/Map;)Lorg/gradle/api/specs/Spec; + public abstract fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; + public abstract fun resolve (Lorg/gradle/api/file/FileCollection;)Lorg/gradle/api/file/FileCollection; +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin : 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 interface annotation class com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator : java/lang/annotation/Annotation { +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Companion; + public fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public final fun copy (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; + public fun equals (Ljava/lang/Object;)Z + public final fun getClassName ()Ljava/lang/String; + public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder { + public fun ()V + public final fun build ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; + public final fun className (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder; + public final fun stats (Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder; +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Companion { + public final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder; +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Companion; + public fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public final fun copy (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; + public fun equals (Ljava/lang/Object;)Z + public final fun getPath ()Ljava/lang/String; + public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder { + public fun ()V + public final fun build ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; + public final fun path (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder; + public final fun stats (Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder; +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Companion { + public final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator$Companion; + public abstract fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; + public abstract fun canRelocateClass (Ljava/lang/String;)Z + public abstract fun canRelocatePath (Ljava/lang/String;)Z + public abstract fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; + public abstract fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; +} + +public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator$Companion { + public final fun getROLE ()Ljava/lang/String; +} + +public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator : com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; + public fun canRelocateClass (Ljava/lang/String;)Z + public fun canRelocatePath (Ljava/lang/String;)Z + public fun exclude (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; + public fun getExcludes ()Ljava/util/Set; + public fun getIncludes ()Ljava/util/Set; + public fun getPathPattern ()Ljava/lang/String; + public fun getPattern ()Ljava/lang/String; + public fun getShadedPathPattern ()Ljava/lang/String; + public fun getShadedPattern ()Ljava/lang/String; + public fun include (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; + public fun isRawString ()Z + public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; + public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; +} + +public class com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest : com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest, org/gradle/api/java/archives/Manifest { + public fun (Lorg/gradle/api/internal/file/FileResolver;)V + public fun (Lorg/gradle/api/internal/file/FileResolver;Lorg/gradle/api/java/archives/internal/DefaultManifest;)V + public synthetic fun (Lorg/gradle/api/internal/file/FileResolver;Lorg/gradle/api/java/archives/internal/DefaultManifest;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun attributes (Ljava/util/Map;)Lorg/gradle/api/java/archives/Manifest; + public fun attributes (Ljava/util/Map;Ljava/lang/String;)Lorg/gradle/api/java/archives/Manifest; + public fun from (Ljava/lang/Object;Lgroovy/lang/Closure;)Lorg/gradle/api/java/archives/Manifest; + public fun from (Ljava/lang/Object;Lorg/gradle/api/Action;)Lorg/gradle/api/java/archives/Manifest; + public fun from ([Ljava/lang/Object;)Lorg/gradle/api/java/archives/Manifest; + public fun getAttributes ()Lorg/gradle/api/java/archives/Attributes; + public synthetic fun getEffectiveManifest ()Lorg/gradle/api/java/archives/Manifest; + public fun getEffectiveManifest ()Lorg/gradle/api/java/archives/internal/DefaultManifest; + public fun getSections ()Ljava/util/Map; + public fun inheritFrom ([Ljava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; + public fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; + public fun writeTo (Ljava/lang/Object;)Lorg/gradle/api/java/archives/Manifest; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest : org/gradle/api/java/archives/Manifest { + public abstract fun inheritFrom ([Ljava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; + public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask : org/gradle/api/DefaultTask { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Companion; + public static final field DESC Ljava/lang/String; + public static final field NAME Ljava/lang/String; + public fun ()V + public final fun knows ()V +} + +public final class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Companion { +} + +public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; + public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/gradle/api/tasks/util/PatternSet;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ZZ)V + public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; +} + +public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$ArchiveFileTreeElement : org/gradle/api/file/FileTreeElement { + public fun (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath;)V + public fun asFileTreeElement ()Lorg/gradle/api/file/FileTreeElement; + public fun copyTo (Ljava/io/File;)Z + public fun copyTo (Ljava/io/OutputStream;)V + public fun getFile ()Ljava/io/File; + public fun getLastModified ()J + public fun getMode ()I + public fun getName ()Ljava/lang/String; + public fun getPath ()Ljava/lang/String; + public fun getPermissions ()Lorg/gradle/api/file/FilePermissions; + public fun getRelativePath ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath; + public synthetic fun getRelativePath ()Lorg/gradle/api/file/RelativePath; + public fun getSize ()J + public fun isClassFile ()Z + public fun isDirectory ()Z + public fun open ()Ljava/io/InputStream; +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$BaseStreamAction : org/gradle/api/internal/file/CopyActionProcessingStreamAction { + public fun ()V + protected final fun isArchive (Lorg/gradle/api/file/FileCopyDetails;)Z + protected final fun isClass (Lorg/gradle/api/file/FileCopyDetails;)Z + public fun processFile (Lorg/gradle/api/internal/file/copy/FileCopyDetailsInternal;)V + protected fun visitDir (Lorg/gradle/api/file/FileCopyDetails;)V + protected abstract fun visitFile (Lorg/gradle/api/file/FileCopyDetails;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion { + public final fun getCONSTANT_TIME_FOR_ZIP_ENTRIES ()J +} + +public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath : org/gradle/api/file/RelativePath { + public fun (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction;Lorg/apache/tools/zip/ZipEntry;)V + public final fun charAt (I)C + public fun get (I)C + public fun getEntry ()Lorg/apache/tools/zip/ZipEntry; + public fun getLength ()I + public fun getParent ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath; + public synthetic fun getParent ()Lorg/gradle/api/file/RelativePath; + public fun isClassFile ()Z + public final fun length ()I +} + +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar : org/gradle/api/tasks/bundling/Jar, com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec { + public fun ()V + public fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + protected fun copy ()V + protected fun createCopyAction ()Lorg/gradle/api/internal/file/copy/CopyAction; + public fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public final fun getApiJars ()Lorg/gradle/api/file/FileCollection; + public final fun getConfigurations ()Ljava/util/List; + public final fun getDependencyFilter ()Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter; + public final fun getIncludedDependencies ()Lorg/gradle/api/file/FileCollection; + public final fun getInternalCompressor ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor; + public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; + public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; + public final fun getRelocationPrefix ()Ljava/lang/String; + public final fun getRelocators ()Ljava/util/List; + public final fun getRootPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; + public final fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/FileCollection; + public fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public final fun getToMinimize ()Lorg/gradle/api/file/FileCollection; + public final fun getTransformers ()Ljava/util/List; + public final fun isEnableRelocation ()Z + public fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun mergeServiceFiles (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun mergeServiceFiles (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun mergeServiceFiles (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun mergeServiceFiles (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun minimize ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun minimize ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun minimize (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun minimize (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun relocate (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public final fun setConfigurations (Ljava/util/List;)V + public final fun setDependencyFilter (Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter;)V + public final fun setEnableRelocation (Z)V + public final fun setRelocationPrefix (Ljava/lang/String;)V + public final fun setRelocators (Ljava/util/List;)V + public final fun setTransformers (Ljava/util/List;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec : org/gradle/api/file/CopySpec { + public abstract fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public abstract fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun mergeServiceFiles (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun mergeServiceFiles (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun minimize ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun minimize (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun relocate (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor : org/gradle/api/internal/file/archive/compression/ArchiveOutputStreamFactory { +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getAddHeader ()Z + public final fun getCopyright ()Ljava/lang/String; + public final fun getEncoding ()Ljava/lang/String; + public final fun getInceptionYear ()Ljava/lang/String; + public final fun getOrganizationName ()Ljava/lang/String; + public final fun getOrganizationURL ()Ljava/lang/String; + public final fun getPreamble1 ()Ljava/lang/String; + public final fun getPreamble2 ()Ljava/lang/String; + public final fun getPreamble3 ()Ljava/lang/String; + public final fun getProjectName ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setAddHeader (Z)V + public final fun setCopyright (Ljava/lang/String;)V + public final fun setEncoding (Ljava/lang/String;)V + public final fun setInceptionYear (Ljava/lang/String;)V + public final fun setOrganizationName (Ljava/lang/String;)V + public final fun setOrganizationURL (Ljava/lang/String;)V + public final fun setPreamble1 (Ljava/lang/String;)V + public final fun setPreamble2 (Ljava/lang/String;)V + public final fun setPreamble3 (Ljava/lang/String;)V + public final fun setProjectName (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getResource ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setResource (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public abstract interface annotation class com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer : java/lang/annotation/Annotation { +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field COMPONENTS_XML_PATH Ljava/lang/String; + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer$Companion; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer$Companion { +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public final fun getResource ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setResource (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getFile ()Ljava/io/File; + public fun getName ()Ljava/lang/String; + public final fun getResource ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setFile (Ljava/io/File;)V + public final fun setResource (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun append (Ljava/lang/String;Ljava/lang/Comparable;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer; + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getAttributes ()Ljava/util/List; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun attributes (Ljava/util/Map;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer; + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getMainClass ()Ljava/lang/String; + public final fun getManifestEntries ()Ljava/util/Map; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setMainClass (Ljava/lang/String;)V + public final fun setManifestEntries (Ljava/util/Map;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/NoOpTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field INSTANCE Lcom/github/jengelman/gradle/plugins/shadow/transformers/NoOpTransformer; + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getCharset ()Ljava/lang/String; + public final fun getKeyTransformer ()Ljava/util/function/Function; + public final fun getMappings ()Ljava/util/Map; + public final fun getMergeSeparator ()Ljava/lang/String; + public final fun getMergeStrategy ()Ljava/lang/String; + public final fun getPaths ()Ljava/util/List; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setCharset (Ljava/lang/String;)V + public final fun setKeyTransformer (Ljava/util/function/Function;)V + public final fun setMappings (Ljava/util/Map;)V + public final fun setMergeSeparator (Ljava/lang/String;)V + public final fun setMergeStrategy (Ljava/lang/String;)V + public final fun setPaths (Ljava/util/List;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer, org/gradle/api/tasks/util/PatternFilterable { + public fun ()V + public fun (Lorg/gradle/api/tasks/util/PatternSet;)V + public synthetic fun (Lorg/gradle/api/tasks/util/PatternSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun exclude (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun getExcludes ()Ljava/util/Set; + public fun getIncludes ()Ljava/util/Set; + public fun hasTransformedResource ()Z + public fun include (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun setExcludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun setIncludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun setPath (Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer$ServiceStream : java/io/ByteArrayOutputStream { + public fun ()V + public fun append (Ljava/io/InputStream;)V + public fun toInputStream ()Ljava/io/InputStream; +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer : org/gradle/api/Named { + public abstract fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; + public abstract fun hasTransformedResource ()Z + public abstract fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion; + public fun (Ljava/lang/String;Ljava/io/InputStream;)V + public fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;)V + public fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public synthetic fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/io/InputStream; + public final fun component3 ()Ljava/util/List; + public final fun component4 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public final fun copy (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public fun equals (Ljava/lang/Object;)Z + public static final fun getEntryTimestamp (ZJ)J + public final fun getInputStream ()Ljava/io/InputStream; + public final fun getPath ()Ljava/lang/String; + public final fun getRelocators ()Ljava/util/List; + public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder { + public fun ()V + public final fun build ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public final fun inputStream (Ljava/io/InputStream;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun path (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun relocators (Ljava/util/List;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun stats (Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion { + public final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun getEntryTimestamp (ZJ)J +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer$Companion; + public static final field XSI_NS Ljava/lang/String; + public fun ()V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getIgnoreDtd ()Z + public final fun getResource ()Ljava/lang/String; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public final fun setIgnoreDtd (Z)V + public final fun setResource (Ljava/lang/String;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer$Companion { +} + diff --git a/build.gradle.kts b/build.gradle.kts index ec16c0a78..687ec92b3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,6 +6,7 @@ plugins { groovy // Required for Spock tests. id("shadow.convention.publish") id("shadow.convention.deploy") + id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.3" id("com.diffplug.spotless") version "7.0.0.BETA4" } From 2396ebe14064782d18fda4aae1ae9b9fdf43ac7f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Nov 2024 09:34:16 +0800 Subject: [PATCH 018/941] chore(deps): update dependency gradle to v8.11.1 (#1035) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 94113f200..e2847c820 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 09cf9652e70b17e57fb5b75d8aa953c9d3c36114 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 21 Nov 2024 11:51:15 +0800 Subject: [PATCH 019/941] Remove Java 11 from test matrix --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce18159a0..4c393c30b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,8 +12,8 @@ jobs: strategy: matrix: os: [ ubuntu-latest, windows-latest ] - # Always test on the latest version and all LTS. - java: [ 11, 17, 21, 23 ] + # Always test on the latest version and some LTS. + java: [ 17, 21, 23 ] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 From 2cae500e42d3a4e9f2c5a6734418d390f85a3366 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Nov 2024 22:56:12 -0500 Subject: [PATCH 020/941] Add AndroidX Gradle plugin lints (#1033) --- build.gradle.kts | 7 ++ lint-baseline.xml | 235 ++++++++++++++++++++++++++++++++++++++++++++ settings.gradle.kts | 14 +++ 3 files changed, 256 insertions(+) create mode 100644 lint-baseline.xml diff --git a/build.gradle.kts b/build.gradle.kts index 687ec92b3..be5b0bef6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -7,6 +7,7 @@ plugins { id("shadow.convention.publish") id("shadow.convention.deploy") id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.3" + id("com.android.lint") version "8.7.2" id("com.diffplug.spotless") version "7.0.0.BETA4" } @@ -27,6 +28,10 @@ kotlin { } } +lint { + baseline = file("lint-baseline.xml") +} + spotless { kotlin { ktlint() @@ -58,6 +63,8 @@ dependencies { testImplementation(platform("org.junit:junit-bom:5.11.3")) testImplementation("org.junit.jupiter:junit-jupiter") testImplementation("org.junit.platform:junit-platform-suite-engine") + + lintChecks("androidx.lint:lint-gradle:1.0.0-alpha02") } val isCI = providers.environmentVariable("CI").isPresent diff --git a/lint-baseline.xml b/lint-baseline.xml new file mode 100644 index 000000000..106b105d7 --- /dev/null +++ b/lint-baseline.xml @@ -0,0 +1,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/settings.gradle.kts b/settings.gradle.kts index 87569ed1f..69acce744 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,6 +1,13 @@ pluginManagement { repositories { mavenCentral() + google { + mavenContent { + includeGroupAndSubgroups("androidx") + includeGroupAndSubgroups("com.android") + includeGroupAndSubgroups("com.google") + } + } gradlePluginPortal() } @@ -24,6 +31,13 @@ develocity { dependencyResolutionManagement { repositories { mavenCentral() + google { + mavenContent { + includeGroupAndSubgroups("androidx") + includeGroupAndSubgroups("com.android") + includeGroupAndSubgroups("com.google") + } + } } } From abcf6aa3254249ea3a960697d6124f75e8a7aef9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 21 Nov 2024 00:35:33 -0500 Subject: [PATCH 021/941] Move ZipCompressor into internal (#1037) --- api/shadow.api | 9 ++++--- lint-baseline.xml | 24 +++++++++---------- .../shadow/internal/DefaultZipCompressor.kt | 1 - .../{tasks => internal}/ZipCompressor.kt | 2 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 1 + .../gradle/plugins/shadow/tasks/ShadowJar.kt | 3 ++- 6 files changed, 20 insertions(+), 20 deletions(-) rename src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/{tasks => internal}/ZipCompressor.kt (70%) diff --git a/api/shadow.api b/api/shadow.api index c847f756e..778fa92cb 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -103,6 +103,9 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/inter public abstract fun resolve (Lorg/gradle/api/file/FileCollection;)Lorg/gradle/api/file/FileCollection; } +public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor : org/gradle/api/internal/file/archive/compression/ArchiveOutputStreamFactory { +} + public abstract class com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin : org/gradle/api/Plugin { public fun ()V public synthetic fun apply (Ljava/lang/Object;)V @@ -233,7 +236,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Co public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; - public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/gradle/api/tasks/util/PatternSet;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ZZ)V + public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/gradle/api/tasks/util/PatternSet;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ZZ)V public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; } @@ -293,7 +296,6 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public final fun getConfigurations ()Ljava/util/List; public final fun getDependencyFilter ()Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter; public final fun getIncludedDependencies ()Lorg/gradle/api/file/FileCollection; - public final fun getInternalCompressor ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor; public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; public final fun getRelocationPrefix ()Ljava/lang/String; @@ -360,9 +362,6 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; } -public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor : org/gradle/api/internal/file/archive/compression/ArchiveOutputStreamFactory { -} - public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z diff --git a/lint-baseline.xml b/lint-baseline.xml index 106b105d7..19d281677 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -85,7 +85,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -96,7 +96,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -107,7 +107,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +118,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,7 +129,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +140,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -151,7 +151,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -162,7 +162,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -173,7 +173,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,7 +184,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -195,7 +195,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -205,7 +205,7 @@ errorLine1="import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory" errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt index d002cb2b4..926df8351 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.internal -import com.github.jengelman.gradle.plugins.shadow.tasks.ZipCompressor import java.io.File import org.apache.tools.zip.Zip64Mode import org.apache.tools.zip.ZipOutputStream diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt similarity index 70% rename from src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt index 3cfc90c5f..a5037af28 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ZipCompressor.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt @@ -1,4 +1,4 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks +package com.github.jengelman.gradle.plugins.shadow.internal import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 030538f0b..00b5ce71c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.impl.RelocatorRemapper import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker +import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index bd8fdcc84..9c51cdf9d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -7,6 +7,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.DefaultZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker +import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator @@ -130,7 +131,7 @@ public abstract class ShadowJar : } @get:Internal - public val internalCompressor: ZipCompressor + internal val internalCompressor: ZipCompressor get() { return when (entryCompression) { ZipEntryCompression.DEFLATED -> DefaultZipCompressor(isZip64, ZipOutputStream.DEFLATED) From 5470da76c43aa527551c2d1f5a9d0489c2d423bc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:04:27 +0800 Subject: [PATCH 022/941] fix(deps): update dependency org.apache.logging.log4j:log4j-core to v2.24.2 (#1038) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index be5b0bef6..262fa1a22 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -50,7 +50,7 @@ dependencies { implementation("org.apache.ant:ant:1.10.15") implementation("org.codehaus.plexus:plexus-utils:4.0.2") implementation("org.codehaus.plexus:plexus-xml:4.0.4") - implementation("org.apache.logging.log4j:log4j-core:2.24.1") + implementation("org.apache.logging.log4j:log4j-core:2.24.2") implementation("org.vafer:jdependency:2.11") testImplementation("org.spockframework:spock-core:2.3-groovy-3.0") { From 0bb2bb0d3d42e3b9911672b34a1577d881de2ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Przemys=C5=82aw=20Ksi=C4=85=C5=BCek?= Date: Mon, 25 Nov 2024 13:39:58 +0100 Subject: [PATCH 023/941] Fix single Log4j2Plugins.dat isn't included into fat jar (#1039) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Przemysław Książek Co-authored-by: Zongle Wang --- src/docs/changes/README.md | 4 ++++ .../shadow/transformers/Log4j2PluginsCacheFileTransformer.kt | 4 +--- .../transformers/Log4j2PluginsCacheFileTransformerSpec.groovy | 4 ++-- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index bdc8c1d01..2c4df94e4 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -11,6 +11,10 @@ - **BREAKING CHANGE:** Remove Develocity integration. ([#1013](https://github.com/GradleUp/shadow/pull/1013)) +**Fixed** + +- Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)). + ## [v8.3.5] (2024-11-03) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index 5fcb1d25d..e3ceadaaf 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -53,9 +53,7 @@ public open class Log4j2PluginsCacheFileTransformer : Transformer { // This functionality matches the original plugin, however, I'm not clear what // the exact logic is. From what I can tell temporaryFiles should be never be empty // if anything has been performed. - val hasTransformedMultipleFiles = temporaryFiles.size > 1 - val hasAtLeastOneFileAndRelocator = temporaryFiles.isNotEmpty() && relocators.isNotEmpty() - return hasTransformedMultipleFiles || hasAtLeastOneFileAndRelocator + return temporaryFiles.isNotEmpty() || relocators.isNotEmpty() } override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy index d767ee75d..716fc1675 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy @@ -25,12 +25,12 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { transformer = new Log4j2PluginsCacheFileTransformer() } - void "should not transformer"() { + void "should transform for a single file"() { when: transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE))) then: - !transformer.hasTransformedResource() + transformer.hasTransformedResource() } void "should transform"() { From 28a6207b1eb18330024f92e0725462cb28266d7a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 25 Nov 2024 08:38:39 -0500 Subject: [PATCH 024/941] Update transformer docs (#1040) * Update Transformer * Update ApacheLicenseResourceTransformer * Update ApacheNoticeResourceTransformer * Update AppendingTransformer * Update ComponentsXmlResourceTransformer * Update DontIncludeResourceTransformer * Update GroovyExtensionModuleTransformer * Update IncludeResourceTransformer * Update Log4j2PluginsCacheFileTransformer * Update ManifestResourceTransformer * Update PropertiesFileTransformer * Update ServiceFileTransformer * Update XmlAppendingTransformer * Cleanups --- .../shadow/transformers/ApacheLicenseResourceTransformer.kt | 4 ++-- .../shadow/transformers/ApacheNoticeResourceTransformer.kt | 2 +- .../plugins/shadow/transformers/AppendingTransformer.kt | 3 +-- .../shadow/transformers/ComponentsXmlResourceTransformer.kt | 2 +- .../shadow/transformers/DontIncludeResourceTransformer.kt | 2 +- .../shadow/transformers/GroovyExtensionModuleTransformer.kt | 5 ++++- .../shadow/transformers/IncludeResourceTransformer.kt | 5 ++--- .../transformers/Log4j2PluginsCacheFileTransformer.kt | 6 +++--- .../shadow/transformers/ManifestResourceTransformer.kt | 2 +- .../shadow/transformers/PropertiesFileTransformer.kt | 2 ++ .../plugins/shadow/transformers/ServiceFileTransformer.kt | 4 ++-- .../gradle/plugins/shadow/transformers/Transformer.kt | 2 +- .../plugins/shadow/transformers/XmlAppendingTransformer.kt | 2 +- .../ApacheLicenseResourceTransformerTest.groovy | 2 +- 14 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index e58350ae5..e00f93562 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -3,9 +3,9 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import org.gradle.api.file.FileTreeElement /** - * Prevents duplicate copies of the license + * Prevents duplicate copies of the license. * - * Modified from `org.apache.maven.plugins.shade.resouce.ApacheLicenseResourceTransformer.java` + * Modified from [org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformer.java). * * @author John Engelman */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 474d99bf2..9afbf3e49 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -14,7 +14,7 @@ import org.gradle.api.tasks.Optional /** * Merges `META-INF/NOTICE.TXT` files. * - * Modified from `org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer.java` + * Modified from [org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformer.java). * * @author John Engelman */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index fcaa2d5a7..e2926f6e8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -10,9 +10,8 @@ import org.gradle.api.tasks.Optional /** * A resource processor that appends content for a resource, separated by a newline. * - * Modified from `org.apache.maven.plugins.shade.resource.AppendingTransformer.java` + * Modified from [org.apache.maven.plugins.shade.resource.AppendingTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/AppendingTransformer.java). * - * Modifications * @author John Engelman */ @CacheableTransformer diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index b917d47b8..345cfaa44 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -17,7 +17,7 @@ import org.gradle.api.file.FileTreeElement /** * A resource processor that aggregates plexus `components.xml` files. * - * Modified from `org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java` + * Modified from [org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformer.java). * * @author John Engelman */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index bc3ee2346..e39a30a04 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -7,7 +7,7 @@ import org.gradle.api.tasks.Optional /** * A resource processor that prevents the inclusion of an arbitrary resource into the shaded JAR. * - * Modified from `org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java` + * Modified from [org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/DontIncludeResourceTransformer.java). * * @author John Engelman */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index 36d3258c3..d0884e210 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -10,7 +10,7 @@ import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement /** - * Modified from `eu.appsatori.gradle.fatjar.tasks.PrepareFiles.groovy` + * Aggregate Apache Groovy extension modules descriptors. * * Resource transformer that merges Groovy extension module descriptor files into a single file. * Groovy extension module descriptor files have the name org.codehaus.groovy.runtime.ExtensionModule @@ -23,6 +23,9 @@ import org.gradle.api.file.FileTreeElement * It will live in the legacy directory (META-INF/services) if all the processed descriptor * files came from the legacy location, otherwise it will be written into the now standard location (META-INF/groovy). * Note that certain JDK9+ tooling will break when using the legacy location. + * + * Modified from [eu.appsatori.gradle.fatjar.tasks.PrepareFiles.groovy](https://github.com/musketyr/gradle-fatjar-plugin/blob/master/src/main/groovy/eu/appsatori/gradle/fatjar/tasks/PrepareFiles.groovy). + * Related to [org.apache.maven.plugins.shade.resource.GroovyResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java). */ @CacheableTransformer public open class GroovyExtensionModuleTransformer : Transformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index 3d5315ef3..14f20385c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -10,10 +10,9 @@ import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity /** - * A resource processor that allows the addition of an arbitrary file - * content into the shaded JAR. + * A resource processor that allows the addition of an arbitrary file content into the shaded JAR. * - * Modified from `org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java` + * Modified from [org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/IncludeResourceTransformer.java). * * @author John Engelman */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index e3ceadaaf..ad1769630 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -18,10 +18,10 @@ import org.gradle.api.file.FileTreeElement /** * Modified from the maven equivalent to work with gradle * + * Modified from [org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer.java](https://github.com/apache/logging-log4j-transform/blob/main/log4j-transform-maven-shade-plugin-extensions/src/main/java/org/apache/logging/log4j/maven/plugins/shade/transformer/Log4j2PluginCacheFileTransformer.java). + * * @author Paul Nelson Baker - * @see [LinkedIn](https://www.linkedin.com/in/paul-n-baker/) - * @see [GitHub](https://github.com/paul-nelson-baker/) - * @see [PluginsCacheFileTransformer.java](https://github.com/edwgiz/maven-shaded-log4j-transformer/blob/master/src/main/java/com/github/edwgiz/mavenShadePlugin/log4j2CacheTransformer/PluginsCacheFileTransformer.java) + * @author John Engelman */ @CacheableTransformer public open class Log4j2PluginsCacheFileTransformer : Transformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index c65d0bf2a..bc03988fc 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -17,7 +17,7 @@ import org.slf4j.LoggerFactory * the first MANIFEST.MF that is found in the set of JARs being processed, or * to a newly created manifest for the shaded JAR. * - * Modified from `org.apache.maven.plugins.shade.resource.ManifestResourceTransformer` + * Modified from [org.apache.maven.plugins.shade.resource.ManifestResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java). * * @author Jason van Zyl * @author John Engelman diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 48844066f..79208cfd5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -86,6 +86,8 @@ import org.gradle.api.tasks.Internal * } * ``` * + * Related to [org.apache.maven.plugins.shade.resource.properties.PropertiesTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/properties/PropertiesTransformer.java). + * * @author Andres Almiray * @author Marc Philipp */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 112e5e734..6d26c2a99 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -15,14 +15,14 @@ import org.gradle.api.tasks.util.PatternFilterable import org.gradle.api.tasks.util.PatternSet /** - * Modified from `org.apache.maven.plugins.shade.resource.ServiceResourceTransformer.java` - * * Resources transformer that appends entries in `META-INF/services` resources into * a single resource. For example, if there are several `META-INF/services/org.apache.maven.project.ProjectBuilder` * resources spread across many JARs the individual entries will all be concatenated into a single * `META-INF/services/org.apache.maven.project.ProjectBuilder` resource packaged into the resultant JAR produced * by the shading process. * + * Modified from [org.apache.maven.plugins.shade.resource.ServicesResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java). + * * @author jvanzyl * @author Charlie Knudsen * @author John Engelman diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index 59056025f..fade7f533 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -6,7 +6,7 @@ import org.gradle.api.file.FileTreeElement import org.gradle.api.tasks.Internal /** - * Modified from `org.apache.maven.plugins.shade.resource.ResourceTransformer.java` + * Modified from [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java). * * @author Jason van Zyl * @author Charlie Knudsen diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index e9fc642df..9694517d3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -18,7 +18,7 @@ import org.xml.sax.InputSource /** * Appends multiple occurrences of some XML file. * - * Modified from `org.apache.maven.plugins.shade.resource.XmlAppendingTransformer.java` + * Modified from [org.apache.maven.plugins.shade.resource.XmlAppendingTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/XmlAppendingTransformer.java). * * @author John Engelman */ diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy index 59a6c62dc..301b8df8a 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy @@ -30,7 +30,7 @@ import static org.junit.jupiter.api.Assertions.* * @author Benjamin Bentmann * @version $Id: ApacheLicenseResourceTransformerTest.java 673906 2008-07-04 05:03:20Z brett $ * - * Modified from org.apache.maven.plugins.shade.resources.ApacheLicenseResourceTransformerTest.java + * Modified from org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformerTest.java */ class ApacheLicenseResourceTransformerTest extends TransformerTestSupport { From 853e5d80bc14242eba4b967d26b3c2d3808544e1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 25 Nov 2024 09:06:45 -0500 Subject: [PATCH 025/941] Add .md support to the Apache License and Notice transformers (#1041) Refs https://github.com/apache/maven-shade-plugin/commit/ba0cc3c317cd3545a5634339c6cd1bfc2491dd52. --- src/docs/changes/README.md | 4 ++++ .../transformers/ApacheLicenseResourceTransformer.kt | 4 +++- .../transformers/ApacheNoticeResourceTransformer.kt | 5 ++++- .../ApacheLicenseResourceTransformerTest.groovy | 2 ++ .../ApacheNoticeResourceTransformerParameterTests.groovy | 9 +++++++++ 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 2c4df94e4..b22c2b02b 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Added** + +- Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) + **Changed** - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index e00f93562..67d4155ad 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -13,11 +13,13 @@ public open class ApacheLicenseResourceTransformer : Transformer by NoOpTransfor override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString return LICENSE_PATH.equals(path, ignoreCase = true) || - LICENSE_TXT_PATH.regionMatches(0, path, 0, LICENSE_TXT_PATH.length, ignoreCase = true) + LICENSE_TXT_PATH.regionMatches(0, path, 0, LICENSE_TXT_PATH.length, ignoreCase = true) || + LICENSE_MD_PATH.regionMatches(0, path, 0, LICENSE_MD_PATH.length, ignoreCase = true) } private companion object { private const val LICENSE_PATH = "META-INF/LICENSE" private const val LICENSE_TXT_PATH = "META-INF/LICENSE.txt" + private const val LICENSE_MD_PATH = "META-INF/LICENSE.md" } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 9afbf3e49..a8412173e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -67,7 +67,9 @@ public open class ApacheNoticeResourceTransformer : Transformer { override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString - return NOTICE_PATH.equals(path, ignoreCase = true) || NOTICE_TXT_PATH.equals(path, ignoreCase = true) + return NOTICE_PATH.equals(path, ignoreCase = true) || + NOTICE_TXT_PATH.equals(path, ignoreCase = true) || + NOTICE_MD_PATH.equals(path, ignoreCase = true) } override fun transform(context: TransformerContext) { @@ -174,5 +176,6 @@ public open class ApacheNoticeResourceTransformer : Transformer { private companion object { private const val NOTICE_PATH = "META-INF/NOTICE" private const val NOTICE_TXT_PATH = "META-INF/NOTICE.txt" + private const val NOTICE_MD_PATH = "META-INF/NOTICE.md" } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy index 301b8df8a..589f2afc3 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy @@ -52,6 +52,8 @@ class ApacheLicenseResourceTransformerTest extends TransformerTestSupport Date: Mon, 25 Nov 2024 12:07:41 -0500 Subject: [PATCH 026/941] Execute ManualCodeSnippetTests to reflect doc chnages (#1042) --- build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 262fa1a22..786c6f381 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -80,6 +80,8 @@ tasks.withType().configureEach { maxHeapSize = "1g" } + // Add src/docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. + inputs.dir(file("src/docs")) systemProperty("shadowVersion", version) // Required to test configuration cache in tests when using withDebug() From d5e3cec0dcff66657cf6d49dc64f510a79527ecb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 26 Nov 2024 00:30:58 -0500 Subject: [PATCH 027/941] Fix lint errors and cleanups (#1043) * Inline UnusedTracker.toMinimize * Optimize jarFile init * Fix SimpleDateFormat usage * Use configureEach * Suppress WithTypeWithoutConfigureEach for ShadowPlugin This reverts commit d1d3e8090b64a6648c09c2084ccabef770a517f1. * Use map for inheritFrom * Suppress EagerGradleConfiguration for inheritFrom This reverts commit 0479a3374f0420f32e8691e69878038491ea9791. * Remove redundant getOrElse * Revert "Remove redundant getOrElse" This reverts commit 1fa1557cbdb4b8284acb3069125f4ac569b98e13. * Replace mainClassName with mainClass * Replace deprecated FileCopyDetails.setMode * Rename configure * Clean up SimpleRelocator * Rearrange createCopyAction * Add JvmDefaultWithCompatibility for Transformer * Tweak style --- api/shadow.api | 6 ++ lint-baseline.xml | 66 ------------------- .../plugins/shadow/ShadowApplicationPlugin.kt | 8 +-- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 1 + .../gradle/plugins/shadow/ShadowPlugin.kt | 2 + .../plugins/shadow/internal/UnusedTracker.kt | 7 +- .../shadow/relocation/SimpleRelocator.kt | 4 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 50 +++++++------- .../ApacheNoticeResourceTransformer.kt | 3 +- .../shadow/transformers/Transformer.kt | 1 + 10 files changed, 45 insertions(+), 103 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 778fa92cb..4679b6b60 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -181,6 +181,8 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat } public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator : com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { + public fun (Ljava/lang/String;Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -553,6 +555,10 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/trans public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } +public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$DefaultImpls { + public static fun getName (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Ljava/lang/String; +} + public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion; public fun (Ljava/lang/String;Ljava/io/InputStream;)V diff --git a/lint-baseline.xml b/lint-baseline.xml index 19d281677..d0dc56411 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -1,50 +1,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 445075c02..d788a9b75 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -50,8 +50,8 @@ public abstract class ShadowApplicationPlugin : Plugin { it.group = ApplicationPlugin.APPLICATION_GROUP it.conventionMapping.map("jvmArgs") { javaApplication.applicationDefaultJvmArgs } it.jarFile.fileProvider( - project.providers.provider { - project.file("${install.get().destinationDir.path}/lib/${shadowJar.get().archiveFile.get().asFile.name}") + install.zip(shadowJar) { i, s -> + project.file("${i.destinationDir.path}/lib/${s.archiveFile.get().asFile.name}") }, ) val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain @@ -70,7 +70,7 @@ public abstract class ShadowApplicationPlugin : Plugin { it.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" it.group = ApplicationPlugin.APPLICATION_GROUP it.classpath = project.files(shadowJar) - it.conventionMapping.map("mainClassName") { javaApplication.mainClass.get() } + it.mainClass.set(javaApplication.mainClass) it.conventionMapping.map("applicationName") { javaApplication.applicationName } it.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile } it.conventionMapping.map("defaultJvmOpts") { javaApplication.applicationDefaultJvmArgs } @@ -100,7 +100,7 @@ public abstract class ShadowApplicationPlugin : Plugin { task.doLast { task.eachFile { if (it.path == "bin/${applicationName.get()}") { - it.mode = 0x755 + it.permissions { permissions -> permissions.unix(755) } } } } 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 ca3a1c2dc..a8de73dfa 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 @@ -78,6 +78,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( shadow.group = ShadowBasePlugin.GROUP_NAME shadow.description = "Create a combined JAR of project and runtime dependencies" shadow.archiveClassifier.set("all") + @Suppress("EagerGradleConfiguration") shadow.manifest.inheritFrom(jarTask.get().manifest) val attrProvider = jarTask.map { it.manifest.attributes["Class-Path"]?.toString().orEmpty() } val files = project.objects.fileCollection().from(shadowConfiguration) 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 c00756f43..e0cde72f2 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 @@ -11,9 +11,11 @@ public abstract class ShadowPlugin : Plugin { override fun apply(project: Project) { project.run { plugins.apply(ShadowBasePlugin::class.java) + @Suppress("WithTypeWithoutConfigureEach") plugins.withType(JavaPlugin::class.java) { plugins.apply(ShadowJavaPlugin::class.java) } + @Suppress("WithTypeWithoutConfigureEach") plugins.withType(ApplicationPlugin::class.java) { plugins.apply(ShadowApplicationPlugin::class.java) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index a23072aa4..ff27e6a75 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -15,7 +15,7 @@ import org.vafer.jdependency.ClazzpathUnit internal class UnusedTracker private constructor( classDirs: Iterable, classJars: FileCollection, - private val _toMinimize: FileCollection, + @get:InputFiles val toMinimize: FileCollection, ) { private val projectUnits: List private val cp = Clazzpath() @@ -24,9 +24,6 @@ internal class UnusedTracker private constructor( projectUnits = classDirs.map { cp.addClazzpathUnit(it) } + classJars.map { cp.addClazzpathUnit(it) } } - @get:InputFiles - val toMinimize: FileCollection get() = _toMinimize - fun findUnused(): Set { val unused = cp.clazzes.toMutableSet() for (cpu in projectUnits) { @@ -37,7 +34,7 @@ internal class UnusedTracker private constructor( } fun addDependency(jarOrDir: File) { - if (_toMinimize.contains(jarOrDir)) { + if (toMinimize.contains(jarOrDir)) { cp.addClazzpathUnit(jarOrDir) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 7e34f2e1e..86c34da73 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -16,8 +16,8 @@ import org.gradle.api.tasks.Optional public open class SimpleRelocator @JvmOverloads constructor( pattern: String?, shadedPattern: String?, - includes: List?, - excludes: List?, + includes: List? = null, + excludes: List? = null, private val _isRawString: Boolean = false, ) : Relocator { private val _pattern: String? diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 9c51cdf9d..cdcc43ab6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -198,28 +198,6 @@ public abstract class ShadowJar : action?.execute(dependencyFilterForMinimize) } - override fun createCopyAction(): CopyAction { - val documentationRegistry = services.get(DocumentationRegistry::class.java) - val unusedTracker = if (minimizeJar) { - UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) - } else { - null - } - return ShadowCopyAction( - archiveFile.get().asFile, - internalCompressor, - documentationRegistry, - metadataCharset, - _transformers, - _relocators, - rootPatternSet, - _stats, - isPreserveFileTimestamps, - minimizeJar, - unusedTracker, - ) - } - override fun dependencies(action: Action?): ShadowJar = apply { action?.execute(_dependencyFilter) } @@ -280,7 +258,7 @@ public abstract class ShadowJar : destination: String, action: Action?, ): ShadowJar = apply { - val relocator = SimpleRelocator(pattern, destination, mutableListOf(), mutableListOf()) + val relocator = SimpleRelocator(pattern, destination) addRelocator(relocator, action) } @@ -307,8 +285,30 @@ public abstract class ShadowJar : logger.info(_stats.toString()) } - private fun addRelocator(relocator: R, configure: Action?) { - configure?.execute(relocator) + override fun createCopyAction(): CopyAction { + val documentationRegistry = services.get(DocumentationRegistry::class.java) + val unusedTracker = if (minimizeJar) { + UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) + } else { + null + } + return ShadowCopyAction( + archiveFile.get().asFile, + internalCompressor, + documentationRegistry, + metadataCharset, + _transformers, + _relocators, + rootPatternSet, + _stats, + isPreserveFileTimestamps, + minimizeJar, + unusedTracker, + ) + } + + private fun addRelocator(relocator: R, action: Action?) { + action?.execute(relocator) _relocators.add(relocator) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index a8412173e..e0c8cbfee 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -4,6 +4,7 @@ import java.io.PrintWriter import java.nio.charset.Charset import java.text.SimpleDateFormat import java.util.Date +import java.util.Locale import java.util.TreeSet import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream @@ -74,7 +75,7 @@ public open class ApacheNoticeResourceTransformer : Transformer { override fun transform(context: TransformerContext) { if (entries.isEmpty()) { - val year = SimpleDateFormat("yyyy").format(Date()).let { + val year = SimpleDateFormat("yyyy", Locale.US).format(Date()).let { if (inceptionYear != it) "$inceptionYear-$it" else it } // add headers diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index fade7f533..0f561829b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -12,6 +12,7 @@ import org.gradle.api.tasks.Internal * @author Charlie Knudsen * @author John Engelman */ +@JvmDefaultWithCompatibility public interface Transformer : Named { public fun canTransformResource(element: FileTreeElement): Boolean From d373be12d69aa33f8023e481d332b137924de5a3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 26 Nov 2024 04:09:17 -0500 Subject: [PATCH 028/941] Resolve Configuration directly in DependencyFilter (#1045) --- api/shadow.api | 2 +- src/docs/changes/README.md | 1 + .../plugins/shadow/internal/AbstractDependencyFilter.kt | 5 ++--- .../gradle/plugins/shadow/internal/DependencyFilter.kt | 9 +++++---- .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 8 +++++--- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 4679b6b60..7a38f335c 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -100,7 +100,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/inter public abstract fun project (Ljava/lang/String;)Lorg/gradle/api/specs/Spec; public abstract fun project (Ljava/util/Map;)Lorg/gradle/api/specs/Spec; public abstract fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; - public abstract fun resolve (Lorg/gradle/api/file/FileCollection;)Lorg/gradle/api/file/FileCollection; + public abstract fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; } public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor : org/gradle/api/internal/file/archive/compression/ArchiveOutputStreamFactory { diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index b22c2b02b..3ecfeb4d5 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -10,6 +10,7 @@ **Changed** - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) +- **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) **Removed** diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt index 74144effe..c0fbaf4af 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -22,16 +22,15 @@ internal sealed class AbstractDependencyFilter( excludedDependencies: MutableSet, ) - override fun resolve(configuration: FileCollection): FileCollection { + override fun resolve(configuration: Configuration): FileCollection { val includedDeps = mutableSetOf() val excludedDeps = mutableSetOf() - configuration as Configuration resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, includedDeps, excludedDeps) return project.files(configuration.files) - project.files(excludedDeps.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) } - override fun resolve(configurations: Collection): FileCollection { + override fun resolve(configurations: Collection): FileCollection { return configurations.map { resolve(it) } .reduceOrNull { acc, fileCollection -> acc + fileCollection } ?: project.files() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt index c89fa8de9..341e3377e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.internal import groovy.lang.Closure +import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.file.FileCollection @@ -8,14 +9,14 @@ import org.gradle.api.specs.Spec public interface DependencyFilter { /** - * Resolve a FileCollection against the include/exclude rules in the filter. + * Resolve a [Configuration] against the include/exclude rules in the filter. */ - public fun resolve(configuration: FileCollection): FileCollection + public fun resolve(configuration: Configuration): FileCollection /** - * Resolve all FileCollections against the include/exclude rules in the filter and combine the results. + * Resolve all [Configuration]s against the include/exclude rules in the filter and combine the results. */ - public fun resolve(configurations: Collection): FileCollection + public fun resolve(configurations: Collection): FileCollection /** * Exclude dependencies that match the provided spec. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index cdcc43ab6..34c35ff6c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -20,6 +20,7 @@ import java.util.concurrent.Callable import java.util.jar.JarFile import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action +import org.gradle.api.artifacts.Configuration import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.FileCollection import org.gradle.api.internal.DocumentationRegistry @@ -47,7 +48,7 @@ public abstract class ShadowJar : ShadowSpec { private val _transformers = mutableListOf() private val _relocators = mutableListOf() - private val _configurations = mutableListOf() + private val _configurations = mutableListOf() private val _stats = ShadowStats() private val _includedDependencies = project.files(Callable { _dependencyFilter.resolve(_configurations) }) @@ -158,7 +159,7 @@ public abstract class ShadowJar : @get:Classpath @get:Optional - public var configurations: List + public var configurations: List get() = _configurations set(value) { _configurations.clear() @@ -327,7 +328,8 @@ public abstract class ShadowJar : private fun configureRelocation() { val packages = mutableSetOf() - configurations.forEach { configuration -> + // Must cast configurations to List to fix type mismatch in runtime. + (configurations as List).forEach { configuration -> configuration.files.forEach { jarFile -> JarFile(jarFile).use { jf -> jf.entries().asSequence().forEach { entry -> From 941e065ed3335dc0c8deadcc8f7de815b43496d6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 26 Nov 2024 04:37:04 -0500 Subject: [PATCH 029/941] Migrate ShadowJar to using lazy properties (#1044) * Migrate public variables in ShadowJar * Rename isEnableRelocation to enableRelocation * Mark AbstractDependencyFilter properties Transient * Remove empty list conventions * Can't migrate relocators to using lazy property * Simplify getters by lazy * Remove @Transient for dependencyFilterForMinimize * More named usages * Optimize get * Migrate FileCollection to ConfigurableFileCollection * Explicit allRelocators * Simplify allRelocators * Update baseline * Note this change * Deprecated isEnableRelocation * Tweak doc * Revert "Deprecated isEnableRelocation" This reverts commit 6d4f3aaf16c15244fa2d3965fe9663351454229e. * Rearrange and tweak styles * Mark configurations as a ListProperty of Configuration --- api/shadow.api | 27 ++- lint-baseline.xml | 8 +- src/docs/changes/README.md | 2 + src/docs/configuration/relocation/README.md | 8 +- src/docs/plugins/README.md | 4 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 14 +- .../internal/AbstractDependencyFilter.kt | 6 +- .../plugins/shadow/internal/UnusedTracker.kt | 3 +- .../gradle/plugins/shadow/internal/Utils.kt | 15 ++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 179 ++++++------------ .../ConfigureShadowRelocationSpec.groovy | 2 +- 11 files changed, 112 insertions(+), 156 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 7a38f335c..2d4ff90ba 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -294,20 +294,21 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar protected fun createCopyAction ()Lorg/gradle/api/internal/file/copy/CopyAction; public fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public final fun getApiJars ()Lorg/gradle/api/file/FileCollection; - public final fun getConfigurations ()Ljava/util/List; - public final fun getDependencyFilter ()Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter; - public final fun getIncludedDependencies ()Lorg/gradle/api/file/FileCollection; + public final fun getApiJars ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getConfigurations ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getDependencyFilter ()Lorg/gradle/api/provider/Property; + public abstract fun getEnableRelocation ()Lorg/gradle/api/provider/Property; + public abstract fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; - public final fun getRelocationPrefix ()Ljava/lang/String; - public final fun getRelocators ()Ljava/util/List; + public abstract fun getMinimizeJar ()Lorg/gradle/api/provider/Property; + public abstract fun getRelocationPrefix ()Lorg/gradle/api/provider/Property; + public abstract fun getRelocators ()Lorg/gradle/api/provider/ListProperty; public final fun getRootPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; - public final fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/FileCollection; + public final fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; - public final fun getToMinimize ()Lorg/gradle/api/file/FileCollection; - public final fun getTransformers ()Ljava/util/List; - public final fun isEnableRelocation ()Z + public final fun getToMinimize ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getTransformers ()Lorg/gradle/api/provider/ListProperty; public fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; @@ -330,12 +331,6 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public final fun setConfigurations (Ljava/util/List;)V - public final fun setDependencyFilter (Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter;)V - public final fun setEnableRelocation (Z)V - public final fun setRelocationPrefix (Ljava/lang/String;)V - public final fun setRelocators (Ljava/util/List;)V - public final fun setTransformers (Ljava/util/List;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; diff --git a/lint-baseline.xml b/lint-baseline.xml index d0dc56411..fd03ccbe4 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -118,7 +118,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,7 +129,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +140,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -151,7 +151,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 3ecfeb4d5..389673961 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -10,6 +10,8 @@ **Changed** - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) +- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) + `isEnableRelocation` is deprecated, use `enableRelocation` instead. - **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) **Removed** diff --git a/src/docs/configuration/relocation/README.md b/src/docs/configuration/relocation/README.md index 73a6d8b48..cb37740d8 100644 --- a/src/docs/configuration/relocation/README.md +++ b/src/docs/configuration/relocation/README.md @@ -66,7 +66,7 @@ Shadow is shipped with a task that can be used to automatically configure all pa This feature was formally shipped into a 2nd plugin (`com.github.johnrengelman.plugin-shadow`) but has been removed for clarity reasons in version 4.0.0. -To configure automatic dependency relocation, set `enableRelocation true` and optionally specify a custom +To configure automatic dependency relocation, set `enableRelocation = true` and optionally specify a custom `relocationPrefix` to override the default value of `"shadow"`. ```groovy @@ -74,8 +74,8 @@ To configure automatic dependency relocation, set `enableRelocation true` and op import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation true - relocationPrefix "myapp" + enableRelocation = true + relocationPrefix = "myapp" } ``` @@ -84,4 +84,4 @@ In versions before 8.1.0 it was necessary to configure a separate `ConfigureShad > Configuring package auto relocation can add significant time to the shadow process as it will process all dependencies in the configurations declared to be shadowed. By default, this is the `runtime` or `runtimeClasspath` configurations. Be mindful that some Gradle plugins will automatically add dependencies to your class path. You may need to remove these -dependencies if you do not intend to shadow them into your library. \ No newline at end of file +dependencies if you do not intend to shadow them into your library. diff --git a/src/docs/plugins/README.md b/src/docs/plugins/README.md index f2858d6da..ab08748f8 100644 --- a/src/docs/plugins/README.md +++ b/src/docs/plugins/README.md @@ -31,7 +31,7 @@ dependencies { } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation true + enableRelocation = true } ``` @@ -56,4 +56,4 @@ The `ConfigureShadowRelocation` task, scans the dependencies from the configurat packages using the specified `prefix` on the associated `ShadowJar` task. While this is useful for developing Gradle plugins, nothing about the `ConfigureShadowRelocation` task is tied to -Gradle projects. It can be used for standard Java or Groovy projects. \ No newline at end of file +Gradle projects. It can be used for standard Java or Groovy projects. 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 a8de73dfa..4b7d869bf 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 @@ -1,5 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter +import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import javax.inject.Inject import org.gradle.api.Plugin @@ -88,11 +90,13 @@ public abstract class ShadowJavaPlugin @Inject constructor( shadow.manifest.attributes["Class-Path"] = attrs.joinToString(" ").trim() } } - shadow.from(sourceSets.getByName("main").output) - shadow.configurations = listOf( - project.configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) - ?: project.configurations.getByName("runtime"), - ) + shadow.from(sourceSets.named("main").map { it.output }) + shadow.enableRelocation.convention(false) + shadow.minimizeJar.convention(false) + shadow.relocationPrefix.convention(ShadowBasePlugin.SHADOW) + shadow.dependencyFilter.convention(DefaultDependencyFilter(project)) + shadow.configurations.convention(listOf(project.runtimeConfiguration)) + shadow.includedDependencies.setFrom(shadow.dependencyFilter.map { it.resolve(shadow.configurations.get()) }) shadow.exclude( "META-INF/INDEX.LIST", "META-INF/*.SF", diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt index c0fbaf4af..58e8ed46b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -11,10 +11,10 @@ import org.gradle.api.specs.Spec import org.gradle.api.specs.Specs internal sealed class AbstractDependencyFilter( - private val project: Project, + @Transient private val project: Project, + @Transient protected val includeSpecs: MutableList> = mutableListOf(), + @Transient protected val excludeSpecs: MutableList> = mutableListOf(), ) : DependencyFilter { - protected val includeSpecs: MutableList> = mutableListOf() - protected val excludeSpecs: MutableList> = mutableListOf() protected abstract fun resolve( dependencies: Set, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index ff27e6a75..78a02537a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -51,8 +51,7 @@ internal class UnusedTracker private constructor( fun getApiJarsFromProject(project: Project): FileCollection { val apiDependencies = project.configurations.findByName("api")?.dependencies ?: return project.files() - val runtimeConfiguration = project.configurations.findByName("runtimeClasspath") - ?: project.configurations.getByName("runtime") + val runtimeConfiguration = project.runtimeConfiguration val apiJars = mutableListOf() apiDependencies.forEach { dep -> when (dep) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 24b0abb5d..8e4128b8d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -1,6 +1,21 @@ package com.github.jengelman.gradle.plugins.shadow.internal import java.io.InputStream +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.plugins.JavaPlugin + +/** + * Return `runtimeClasspath` or `runtime` configuration. + */ +internal inline val Project.runtimeConfiguration: Configuration get() { + return configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) + ?: configurations.getByName("runtime") +} + +@Suppress("NOTHING_TO_INLINE") +internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy = + lazy(LazyThreadSafetyMode.NONE, initializer) internal fun Class<*>.requireResourceAsText(name: String): String { return requireResourceAsStream(name).bufferedReader().readText() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 34c35ff6c..c2797ef02 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -1,13 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow.tasks -import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.DefaultZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor +import com.github.jengelman.gradle.plugins.shadow.internal.unsafeLazy import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator @@ -16,17 +15,19 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransfor import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer -import java.util.concurrent.Callable import java.util.jar.JarFile import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.FileCollection import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.DefaultCopySpec +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Classpath import org.gradle.api.tasks.Input @@ -46,90 +47,58 @@ import org.gradle.api.tasks.util.PatternSet public abstract class ShadowJar : Jar(), ShadowSpec { - private val _transformers = mutableListOf() - private val _relocators = mutableListOf() - private val _configurations = mutableListOf() - private val _stats = ShadowStats() - private val _includedDependencies = project.files(Callable { _dependencyFilter.resolve(_configurations) }) - - @Transient private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) - private var minimizeJar = false - private var _isEnableRelocation = false - private var _relocationPrefix = ShadowBasePlugin.SHADOW - private var _toMinimize: FileCollection? = null - private var _apiJars: FileCollection? = null - private var _sourceSetsClassesDirs: FileCollection? = null - - @Transient - private var _dependencyFilter: DependencyFilter = DefaultDependencyFilter(project) - init { + // shadow filters out files later. This was the default behavior in Gradle < 6.x duplicatesStrategy = DuplicatesStrategy.INCLUDE manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) - inputs.property("minimize") { minimizeJar } outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { - _transformers.any { !isCacheableTransform(it::class.java) } || - _relocators.any { !isCacheableRelocator(it::class.java) } + transformers.get().any { !isCacheableTransform(it::class.java) } || + relocators.get().any { !isCacheableRelocator(it::class.java) } } } @get:Internal - override val stats: ShadowStats get() = _stats + override val stats: ShadowStats = ShadowStats() @get:Classpath - public val toMinimize: FileCollection - get() { - if (_toMinimize == null) { - _toMinimize = if (minimizeJar) { - dependencyFilterForMinimize.resolve(_configurations) - .minus(apiJars) - } else { - project.objects.fileCollection() - } + public val toMinimize: ConfigurableFileCollection by unsafeLazy { + project.objects.fileCollection().apply { + if (minimizeJar.get()) { + setFrom(dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) } - return _toMinimize!! } + } @get:Classpath - public val apiJars: FileCollection - get() { - if (_apiJars == null) { - _apiJars = if (minimizeJar) { - UnusedTracker.getApiJarsFromProject(project) - } else { - project.objects.fileCollection() - } + public val apiJars: ConfigurableFileCollection by unsafeLazy { + project.objects.fileCollection().apply { + if (minimizeJar.get()) { + setFrom(UnusedTracker.getApiJarsFromProject(project)) } - return _apiJars!! } + } @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) - public val sourceSetsClassesDirs: FileCollection - get() { - if (_sourceSetsClassesDirs == null) { - val allClassesDirs = project.objects.fileCollection() - if (minimizeJar) { - project.extensions.getByType(SourceSetContainer::class.java).forEach { sourceSet -> - allClassesDirs.from(sourceSet.output.classesDirs) - } + public val sourceSetsClassesDirs: ConfigurableFileCollection by unsafeLazy { + project.objects.fileCollection().apply { + if (minimizeJar.get()) { + project.extensions.getByType(SourceSetContainer::class.java).forEach { sourceSet -> + from(sourceSet.output.classesDirs.filter { it.isDirectory }) } - _sourceSetsClassesDirs = allClassesDirs.filter { it.isDirectory } } - return _sourceSetsClassesDirs!! } + } @get:Classpath - public val includedDependencies: FileCollection get() = _includedDependencies + public abstract val includedDependencies: ConfigurableFileCollection @get:Internal public val rootPatternSet: PatternSet - get() { - return (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet - } + get() = (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet @get:Internal internal val internalCompressor: ZipCompressor @@ -142,56 +111,32 @@ public abstract class ShadowJar : } @get:Nested - public var transformers: List - get() = _transformers - set(value) { - _transformers.clear() - _transformers.addAll(value) - } + public abstract val transformers: ListProperty @get:Nested - public var relocators: List - get() = _relocators - set(value) { - _relocators.clear() - _relocators.addAll(value) - } + public abstract val relocators: ListProperty @get:Classpath @get:Optional - public var configurations: List - get() = _configurations - set(value) { - _configurations.clear() - _configurations.addAll(value) - } + public abstract val configurations: ListProperty @get:Internal - public var dependencyFilter: DependencyFilter - get() = _dependencyFilter - set(value) { - _dependencyFilter = value - } + public abstract val dependencyFilter: Property @get:Input - public var isEnableRelocation: Boolean - get() = _isEnableRelocation - set(value) { - _isEnableRelocation = value - } + public abstract val enableRelocation: Property @get:Input - public var relocationPrefix: String - get() = _relocationPrefix - set(value) { - _relocationPrefix = value - } + public abstract val relocationPrefix: Property + + @get:Input + public abstract val minimizeJar: Property @Internal - override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest + override fun getManifest(): InheritManifest = super.manifest as InheritManifest override fun minimize(): ShadowJar = apply { - minimizeJar = true + minimizeJar.set(true) } override fun minimize(action: Action?): ShadowJar = apply { @@ -200,7 +145,7 @@ public abstract class ShadowJar : } override fun dependencies(action: Action?): ShadowJar = apply { - action?.execute(_dependencyFilter) + action?.execute(dependencyFilter.get()) } override fun transform(clazz: Class): ShadowJar { @@ -278,17 +223,14 @@ public abstract class ShadowJar : @TaskAction override fun copy() { - if (_isEnableRelocation) { - configureRelocation() - } - from(_includedDependencies) + from(includedDependencies) super.copy() - logger.info(_stats.toString()) + logger.info(stats.toString()) } override fun createCopyAction(): CopyAction { val documentationRegistry = services.get(DocumentationRegistry::class.java) - val unusedTracker = if (minimizeJar) { + val unusedTracker = if (minimizeJar.get()) { UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) } else { null @@ -298,24 +240,24 @@ public abstract class ShadowJar : internalCompressor, documentationRegistry, metadataCharset, - _transformers, - _relocators, + transformers.get(), + relocators.get() + packageRelocators, rootPatternSet, - _stats, + stats, isPreserveFileTimestamps, - minimizeJar, + minimizeJar.get(), unusedTracker, ) } private fun addRelocator(relocator: R, action: Action?) { action?.execute(relocator) - _relocators.add(relocator) + relocators.add(relocator) } private fun addTransform(transformer: T, action: Action?) { action?.execute(transformer) - _transformers.add(transformer) + transformers.add(transformer) } private fun isCacheableRelocator(clazz: Class): Boolean { @@ -326,22 +268,21 @@ public abstract class ShadowJar : return clazz.isAnnotationPresent(CacheableTransformer::class.java) } - private fun configureRelocation() { - val packages = mutableSetOf() - // Must cast configurations to List to fix type mismatch in runtime. - (configurations as List).forEach { configuration -> - configuration.files.forEach { jarFile -> - JarFile(jarFile).use { jf -> - jf.entries().asSequence().forEach { entry -> - if (entry.name.endsWith(".class") && entry.name != "module-info.class") { - packages.add(entry.name.substringBeforeLast('/').replace('/', '.')) - } + private val packageRelocators: List + get() { + if (!enableRelocation.get()) return emptyList() + + val prefix = relocationPrefix.get() + // Must cast configurations to List to fix type mismatch in runtime. + return (configurations.get() as List).flatMap { configuration -> + configuration.files.flatMap { file -> + JarFile(file).use { jarFile -> + jarFile.entries().toList() + .filter { it.name.endsWith(".class") && it.name != "module-info.class" } + .map { it.name.substringBeforeLast('/').replace('/', '.') } + .map { SimpleRelocator(it, "$prefix.$it") } } } } } - packages.forEach { - relocate(it, "$_relocationPrefix.$it") - } - } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy index bcb1a6981..c7212ddbb 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy @@ -9,7 +9,7 @@ class ConfigureShadowRelocationSpec extends PluginSpecification { given: buildFile << """ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation true + enableRelocation = true } dependencies { From db1ae878afc76697b2f2a9f7ba8ed390739ce626 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 26 Nov 2024 05:21:16 -0500 Subject: [PATCH 030/941] Reformat test code to reduce diffs (#1046) --- .editorconfig | 3 + .../plugins/shadow/ApplicationSpec.groovy | 50 ++-- .../shadow/ConfigurationCacheSpec.groovy | 18 +- .../ConfigureShadowRelocationSpec.groovy | 32 +- .../plugins/shadow/FilteringSpec.groovy | 96 +++--- .../plugins/shadow/PublishingSpec.groovy | 48 +-- .../plugins/shadow/RelocationSpec.groovy | 170 +++++------ .../plugins/shadow/ShadowPluginSpec.groovy | 132 ++++----- .../plugins/shadow/TransformerSpec.groovy | 276 +++++++++--------- .../shadow/caching/AbstractCachingSpec.groovy | 7 +- .../caching/MinimizationCachingSpec.groovy | 14 +- .../caching/RelocationCachingSpec.groovy | 16 +- .../caching/ShadowJarCachingSpec.groovy | 30 +- .../caching/TransformCachingSpec.groovy | 68 ++--- .../shadow/docs/ManualCodeSnippetTests.groovy | 6 +- .../extractor/ManualSnippetExtractor.groovy | 2 +- .../executer/ExceptionTransformer.groovy | 58 ++-- .../snippets/executer/SnippetExecuter.java | 2 +- .../fixture/GroovyScriptFixture.groovy | 8 +- .../SimpleRelocatorParameterTest.groovy | 2 +- .../relocation/SimpleRelocatorTest.groovy | 16 +- ...pacheLicenseResourceTransformerTest.groovy | 3 +- ...ceResourceTransformerParameterTests.groovy | 13 +- ...ApacheNoticeResourceTransformerTest.groovy | 3 +- .../AppendingTransformerTest.groovy | 3 +- ...omponentsXmlResourceTransformerTest.groovy | 33 +-- ...g4j2PluginsCacheFileTransformerSpec.groovy | 1 - .../ManifestAppenderTransformerTest.groovy | 4 +- .../PropertiesFileTransformerSpec.groovy | 4 +- .../PropertiesFileTransformerTest.groovy | 6 +- .../ServiceFileTransformerSpec.groovy | 60 ++-- .../XmlAppendingTransformerTest.groovy | 3 +- .../gradle/plugins/shadow/util/HashUtil.java | 1 + .../shadow/util/PluginSpecification.groovy | 13 +- .../plugins/shadow/util/file/TestFile.java | 7 +- .../shadow/util/file/TestFileHelper.groovy | 4 +- .../util/file/TestWorkspaceBuilder.groovy | 3 +- .../repo/maven/AbstractMavenModule.groovy | 6 +- .../repo/maven/DefaultMavenMetaData.groovy | 2 +- .../shadow/util/repo/maven/MavenPom.groovy | 12 +- 40 files changed, 623 insertions(+), 612 deletions(-) diff --git a/.editorconfig b/.editorconfig index f36ddfdde..97c1b8453 100755 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,9 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true +[*.{groovy,java}] +indent_size = 4 + [*.{kt,kts}] ij_kotlin_imports_layout = * ij_kotlin_allow_trailing_comma = true diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy index 00f2c51fc..ff6f72cb6 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy @@ -13,9 +13,9 @@ class ApplicationSpec extends PluginSpecification { def 'integration with application plugin'() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() file('src/main/java/myapp/Main.java') << """ package myapp; @@ -32,11 +32,11 @@ class ApplicationSpec extends PluginSpecification { application { mainClass = 'myapp.Main' } - + dependencies { implementation 'shadow:a:1.0' } - + runShadow { args 'foo' } @@ -76,9 +76,9 @@ class ApplicationSpec extends PluginSpecification { def 'integration with application plugin and java toolchains'() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() file('src/main/java/myapp/Main.java') << """ package myapp; @@ -91,35 +91,35 @@ class ApplicationSpec extends PluginSpecification { buildFile << """ apply plugin: 'application' - + application { mainClass = 'myapp.Main' } - + dependencies { implementation 'shadow:a:1.0' } - + java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } - + runShadow { args 'foo' doFirst { logger.lifecycle("Running application with JDK \${it.javaLauncher.get().metadata.languageVersion.asInt()}") } - } + } """.stripIndent() - settingsFile.write """ + settingsFile.write """ plugins { // https://docs.gradle.org/8.0.1/userguide/toolchains.html#sub:download_repositories id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0") } - + rootProject.name = 'myapp' """.stripIndent() @@ -157,9 +157,9 @@ class ApplicationSpec extends PluginSpecification { def 'shadow application distributions should use shadow jar'() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() file('src/main/java/myapp/Main.java') << """ package myapp; @@ -176,11 +176,11 @@ class ApplicationSpec extends PluginSpecification { application { mainClass = 'myapp.Main' } - + dependencies { shadow 'shadow:a:1.0' } - + runShadow { args 'foo' } @@ -209,9 +209,9 @@ class ApplicationSpec extends PluginSpecification { def 'installShadow does not execute dependent shadow task'() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() file('src/main/java/myapp/Main.java') << """ package myapp; @@ -228,11 +228,11 @@ class ApplicationSpec extends PluginSpecification { application { mainClass = 'myapp.Main' } - + dependencies { implementation 'shadow:a:1.0' } - + runShadow { args 'foo' } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy index f05a6170b..af3a000ea 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy @@ -7,12 +7,12 @@ class ConfigurationCacheSpec extends PluginSpecification { @Override def setup() { repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() + .insertFile('b.properties', 'b') + .publish() buildFile << """ dependencies { @@ -39,11 +39,11 @@ class ConfigurationCacheSpec extends PluginSpecification { application { mainClass = 'myapp.Main' } - + dependencies { implementation 'shadow:a:1.0' } - + runShadow { args 'foo' } @@ -127,8 +127,8 @@ class ConfigurationCacheSpec extends PluginSpecification { then: output.exists() contains(output, [ - 'server/Server.class', - 'junit/framework/Test.class' + 'server/Server.class', + 'junit/framework/Test.class' ]) doesNotContain(output, ['client/Client.class']) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy index c7212ddbb..2c5af9c15 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy @@ -22,22 +22,22 @@ class ConfigureShadowRelocationSpec extends PluginSpecification { then: contains(output, [ - 'META-INF/MANIFEST.MF', - 'shadow/junit/textui/ResultPrinter.class', - 'shadow/junit/textui/TestRunner.class', - 'shadow/junit/framework/Assert.class', - 'shadow/junit/framework/AssertionFailedError.class', - 'shadow/junit/framework/ComparisonCompactor.class', - 'shadow/junit/framework/ComparisonFailure.class', - 'shadow/junit/framework/Protectable.class', - 'shadow/junit/framework/Test.class', - 'shadow/junit/framework/TestCase.class', - 'shadow/junit/framework/TestFailure.class', - 'shadow/junit/framework/TestListener.class', - 'shadow/junit/framework/TestResult$1.class', - 'shadow/junit/framework/TestResult.class', - 'shadow/junit/framework/TestSuite$1.class', - 'shadow/junit/framework/TestSuite.class' + 'META-INF/MANIFEST.MF', + 'shadow/junit/textui/ResultPrinter.class', + 'shadow/junit/textui/TestRunner.class', + 'shadow/junit/framework/Assert.class', + 'shadow/junit/framework/AssertionFailedError.class', + 'shadow/junit/framework/ComparisonCompactor.class', + 'shadow/junit/framework/ComparisonFailure.class', + 'shadow/junit/framework/Protectable.class', + 'shadow/junit/framework/Test.class', + 'shadow/junit/framework/TestCase.class', + 'shadow/junit/framework/TestFailure.class', + 'shadow/junit/framework/TestListener.class', + 'shadow/junit/framework/TestResult$1.class', + 'shadow/junit/framework/TestResult.class', + 'shadow/junit/framework/TestSuite$1.class', + 'shadow/junit/framework/TestSuite.class' ]) } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy index 7958c3656..970e33e8b 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy @@ -11,12 +11,12 @@ class FilteringSpec extends PluginSpecification { @Override def setup() { repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() + .insertFile('b.properties', 'b') + .publish() buildFile << """ dependencies { @@ -58,19 +58,19 @@ class FilteringSpec extends PluginSpecification { def "exclude dependency"() { given: repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() + .insertFile('c.properties', 'c') + .publish() repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() + .insertFile('d.properties', 'd') + .dependsOn('c') + .publish() buildFile << ''' // tag::excludeDep[] dependencies { implementation 'shadow:d:1.0' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('shadow:d:1.0')) @@ -93,19 +93,19 @@ class FilteringSpec extends PluginSpecification { def "exclude dependency using wildcard syntax"() { given: repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() + .insertFile('c.properties', 'c') + .publish() repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() + .insertFile('d.properties', 'd') + .dependsOn('c') + .publish() buildFile << ''' // tag::excludeDepWildcard[] dependencies { implementation 'shadow:d:1.0' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('shadow:d:.*')) @@ -129,18 +129,18 @@ class FilteringSpec extends PluginSpecification { def "dependency exclusions affect UP-TO-DATE check"() { given: repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() + .insertFile('c.properties', 'c') + .publish() repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() + .insertFile('d.properties', 'd') + .dependsOn('c') + .publish() buildFile << ''' dependencies { implementation 'shadow:d:1.0' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('shadow:d:1.0')) @@ -159,7 +159,7 @@ class FilteringSpec extends PluginSpecification { when: 'Update build file shadowJar dependency exclusion' buildFile.text = buildFile.text.replace('exclude(dependency(\'shadow:d:1.0\'))', - 'exclude(dependency(\'shadow:c:1.0\'))') + 'exclude(dependency(\'shadow:c:1.0\'))') BuildResult result = run('shadowJar') @@ -178,18 +178,18 @@ class FilteringSpec extends PluginSpecification { def "project exclusions affect UP-TO-DATE check"() { given: repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() + .insertFile('c.properties', 'c') + .publish() repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() + .insertFile('d.properties', 'd') + .dependsOn('c') + .publish() buildFile << ''' dependencies { implementation 'shadow:d:1.0' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('shadow:d:1.0')) @@ -228,12 +228,12 @@ class FilteringSpec extends PluginSpecification { def "include dependency, excluding all others"() { given: repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() + .insertFile('c.properties', 'c') + .publish() repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() + .insertFile('d.properties', 'd') + .dependsOn('c') + .publish() file('src/main/java/shadow/Passed.java') << ''' package shadow; @@ -244,7 +244,7 @@ class FilteringSpec extends PluginSpecification { dependencies { implementation 'shadow:d:1.0' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { include(dependency('shadow:d:1.0')) @@ -293,7 +293,7 @@ class FilteringSpec extends PluginSpecification { dependencies { implementation project(':client') } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(project(':client')) @@ -310,7 +310,7 @@ class FilteringSpec extends PluginSpecification { then: serverOutput.exists() doesNotContain(serverOutput, [ - 'client/Client.class', + 'client/Client.class', ]) and: @@ -364,13 +364,13 @@ class FilteringSpec extends PluginSpecification { then: serverOutput.exists() doesNotContain(serverOutput, [ - 'junit/framework/Test.class' + 'junit/framework/Test.class' ]) and: contains(serverOutput, [ - 'client/Client.class', - 'server/Server.class']) + 'client/Client.class', + 'server/Server.class']) } //http://mail-archives.apache.org/mod_mbox/ant-user/200506.mbox/%3C001d01c57756$6dc35da0$dc00a8c0@CTEGDOMAIN.COM%3E @@ -400,19 +400,19 @@ class FilteringSpec extends PluginSpecification { def "handle exclude with circular dependency"() { given: repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .dependsOn('d') - .publish() + .insertFile('c.properties', 'c') + .dependsOn('d') + .publish() repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() + .insertFile('d.properties', 'd') + .dependsOn('c') + .publish() buildFile << ''' dependencies { implementation 'shadow:d:1.0' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('shadow:d:1.0')) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy index d79a07c68..8787c532b 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy @@ -20,12 +20,12 @@ class PublishingSpec extends PluginSpecification { def "publish shadow jar with maven-publish plugin"() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() + .insertFile('b.properties', 'b') + .publish() settingsFile << "rootProject.name = 'maven'" buildFile << """ @@ -35,12 +35,12 @@ class PublishingSpec extends PluginSpecification { implementation 'shadow:a:1.0' shadow 'shadow:b:1.0' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { archiveClassifier = '' archiveBaseName = 'maven-all' } - + publishing { publications { shadow(MavenPublication) { @@ -81,18 +81,18 @@ class PublishingSpec extends PluginSpecification { } @Issue([ - "https://github.com/GradleUp/shadow/issues/860", - "https://github.com/GradleUp/shadow/issues/945", + "https://github.com/GradleUp/shadow/issues/860", + "https://github.com/GradleUp/shadow/issues/945", ]) def "publish shadow jar with maven-publish plugin using custom classifier and extension"() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() + .insertFile('b.properties', 'b') + .publish() settingsFile << "rootProject.name = 'maven'" buildFile << """ @@ -102,7 +102,7 @@ class PublishingSpec extends PluginSpecification { implementation 'shadow:a:1.0' shadow 'shadow:b:1.0' } - + publishing { publications { shadow(MavenPublication) { publication -> @@ -116,7 +116,7 @@ class PublishingSpec extends PluginSpecification { } } } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { archiveClassifier = 'my-classifier' archiveExtension = 'my-ext' @@ -149,7 +149,7 @@ class PublishingSpec extends PluginSpecification { version = "1.0" group = 'shadow' - + repositories { maven { url "${repo.uri}" } } publishing { repositories { @@ -184,7 +184,7 @@ class PublishingSpec extends PluginSpecification { plugins { id 'com.gradleup.shadow' } - + dependencies { implementation project(':a') shadow project(':b') @@ -194,7 +194,7 @@ class PublishingSpec extends PluginSpecification { archiveClassifier = '' archiveBaseName = 'maven-all' } - + publishing { publications { shadow(MavenPublication) { @@ -232,12 +232,12 @@ class PublishingSpec extends PluginSpecification { def "publish shadow jar with maven-publish plugin and Gradle metadata"() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() + .insertFile('b.properties', 'b') + .publish() settingsFile << """ rootProject.name = 'maven' diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy index b9f522345..2915cc8f4 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy @@ -16,7 +16,7 @@ class RelocationSpec extends PluginSpecification { dependencies { implementation 'junit:junit:3.8.2' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { relocate 'junit.textui', 'a' relocate 'junit.framework', 'b' @@ -31,41 +31,41 @@ class RelocationSpec extends PluginSpecification { then: contains(output, [ - 'META-INF/MANIFEST.MF', - 'a/ResultPrinter.class', - 'a/TestRunner.class', - 'b/Assert.class', - 'b/AssertionFailedError.class', - 'b/ComparisonCompactor.class', - 'b/ComparisonFailure.class', - 'b/Protectable.class', - 'b/Test.class', - 'b/TestCase.class', - 'b/TestFailure.class', - 'b/TestListener.class', - 'b/TestResult$1.class', - 'b/TestResult.class', - 'b/TestSuite$1.class', - 'b/TestSuite.class' + 'META-INF/MANIFEST.MF', + 'a/ResultPrinter.class', + 'a/TestRunner.class', + 'b/Assert.class', + 'b/AssertionFailedError.class', + 'b/ComparisonCompactor.class', + 'b/ComparisonFailure.class', + 'b/Protectable.class', + 'b/Test.class', + 'b/TestCase.class', + 'b/TestFailure.class', + 'b/TestListener.class', + 'b/TestResult$1.class', + 'b/TestResult.class', + 'b/TestSuite$1.class', + 'b/TestSuite.class' ]) and: doesNotContain(output, [ - 'junit/textui/ResultPrinter.class', - 'junit/textui/TestRunner.class', - 'junit/framework/Assert.class', - 'junit/framework/AssertionFailedError.class', - 'junit/framework/ComparisonCompactor.class', - 'junit/framework/ComparisonFailure.class', - 'junit/framework/Protectable.class', - 'junit/framework/Test.class', - 'junit/framework/TestCase.class', - 'junit/framework/TestFailure.class', - 'junit/framework/TestListener.class', - 'junit/framework/TestResult$1.class', - 'junit/framework/TestResult.class', - 'junit/framework/TestSuite$1.class', - 'junit/framework/TestSuite.class' + 'junit/textui/ResultPrinter.class', + 'junit/textui/TestRunner.class', + 'junit/framework/Assert.class', + 'junit/framework/AssertionFailedError.class', + 'junit/framework/ComparisonCompactor.class', + 'junit/framework/ComparisonFailure.class', + 'junit/framework/Protectable.class', + 'junit/framework/Test.class', + 'junit/framework/TestCase.class', + 'junit/framework/TestFailure.class', + 'junit/framework/TestListener.class', + 'junit/framework/TestResult$1.class', + 'junit/framework/TestResult.class', + 'junit/framework/TestSuite$1.class', + 'junit/framework/TestSuite.class' ]) and: 'Test that manifest file exists with contents' @@ -81,7 +81,7 @@ class RelocationSpec extends PluginSpecification { dependencies { implementation 'junit:junit:3.8.2' } - + // tag::relocateFilter[] tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { relocate('junit.textui', 'a') { @@ -99,35 +99,35 @@ class RelocationSpec extends PluginSpecification { then: contains(output, [ - 'a/ResultPrinter.class', - 'b/Test.class', - 'b/TestCase.class', - 'b/TestFailure.class', - 'b/TestListener.class', - 'b/TestResult$1.class', - 'b/TestResult.class', - 'b/TestSuite$1.class', - 'b/TestSuite.class' + 'a/ResultPrinter.class', + 'b/Test.class', + 'b/TestCase.class', + 'b/TestFailure.class', + 'b/TestListener.class', + 'b/TestResult$1.class', + 'b/TestResult.class', + 'b/TestSuite$1.class', + 'b/TestSuite.class' ]) and: doesNotContain(output, [ - 'a/TestRunner.class', - 'b/Assert.class', - 'b/AssertionFailedError.class', - 'b/ComparisonCompactor.class', - 'b/ComparisonFailure.class', - 'b/Protectable.class' + 'a/TestRunner.class', + 'b/Assert.class', + 'b/AssertionFailedError.class', + 'b/ComparisonCompactor.class', + 'b/ComparisonFailure.class', + 'b/Protectable.class' ]) and: contains(output, [ - 'junit/textui/TestRunner.class', - 'junit/framework/Assert.class', - 'junit/framework/AssertionFailedError.class', - 'junit/framework/ComparisonCompactor.class', - 'junit/framework/ComparisonFailure.class', - 'junit/framework/Protectable.class' + 'junit/textui/TestRunner.class', + 'junit/framework/Assert.class', + 'junit/framework/AssertionFailedError.class', + 'junit/framework/ComparisonCompactor.class', + 'junit/framework/ComparisonFailure.class', + 'junit/framework/Protectable.class' ]) } @@ -138,7 +138,7 @@ class RelocationSpec extends PluginSpecification { dependencies { implementation 'junit:junit:3.8.2' } - + // tag::relocate[] tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { relocate 'junit.framework', 'shadow.junit' @@ -148,7 +148,7 @@ class RelocationSpec extends PluginSpecification { file('src/main/java/shadow/ShadowTest.java') << ''' package shadow; - + import junit.framework.Test; import junit.framework.TestResult; public class ShadowTest implements Test { @@ -162,21 +162,21 @@ class RelocationSpec extends PluginSpecification { then: contains(output, [ - 'shadow/ShadowTest.class', - 'shadow/junit/Test.class', - 'shadow/junit' + 'shadow/ShadowTest.class', + 'shadow/junit/Test.class', + 'shadow/junit' ]) and: doesNotContain(output, [ - 'junit/framework', - 'junit/framework/Test.class' + 'junit/framework', + 'junit/framework/Test.class' ]) and: 'check that the class can be loaded. If the file was not relocated properly, we should get a NoDefClassFound' // Isolated class loader with only the JVM system jars and the output jar from the test project URLClassLoader classLoader = new URLClassLoader([output.toURI().toURL()] as URL[], - ClassLoader.systemClassLoader.parent) + ClassLoader.systemClassLoader.parent) classLoader.loadClass('shadow.ShadowTest') } @@ -185,7 +185,7 @@ class RelocationSpec extends PluginSpecification { given: 'Core project with dependency and resource' file('core/build.gradle') << """ apply plugin: 'java-library' - + repositories { maven { url "${repo.uri}" } } dependencies { api 'junit:junit:3.8.2' } """.stripIndent() @@ -194,9 +194,9 @@ class RelocationSpec extends PluginSpecification { file('core/src/main/resources/test.properties') << 'name=test' file('core/src/main/java/core/Core.java') << ''' package core; - + import junit.framework.Test; - + public class Core {} '''.stripIndent() @@ -204,10 +204,10 @@ class RelocationSpec extends PluginSpecification { file('app/build.gradle') << """ apply plugin: 'java' apply plugin: 'com.gradleup.shadow' - + repositories { maven { url "${repo.uri}" } } dependencies { implementation project(':core') } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { relocate 'core', 'app.core' relocate 'junit.framework', 'app.junit.framework' @@ -217,10 +217,10 @@ class RelocationSpec extends PluginSpecification { file('app/src/main/resources/APP-TEST') << 'APP TEST RESOURCE' file('app/src/main/java/app/App.java') << ''' package app; - + import core.Core; import junit.framework.Test; - + public class App {} '''.stripIndent() @@ -238,12 +238,12 @@ class RelocationSpec extends PluginSpecification { and: contains(appOutput, [ - 'TEST', - 'APP-TEST', - 'test.properties', - 'app/core/Core.class', - 'app/App.class', - 'app/junit/framework/Test.class' + 'TEST', + 'APP-TEST', + 'test.properties', + 'app/core/Core.class', + 'app/App.class', + 'app/junit/framework/Test.class' ]) } @@ -251,11 +251,11 @@ class RelocationSpec extends PluginSpecification { def "relocate resource files"() { given: repo.module('shadow', 'dep', '1.0') - .insertFile('foo/dep.properties', 'c') - .publish() + .insertFile('foo/dep.properties', 'c') + .publish() file('src/main/java/foo/Foo.java') << ''' package foo; - + class Foo {} '''.stripIndent() file('src/main/resources/foo/foo.properties') << 'name=foo' @@ -264,7 +264,7 @@ class RelocationSpec extends PluginSpecification { dependencies { implementation 'shadow:dep:1.0' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { relocate 'foo', 'bar' } @@ -275,16 +275,16 @@ class RelocationSpec extends PluginSpecification { then: contains(output, [ - 'bar/Foo.class', - 'bar/foo.properties', - 'bar/dep.properties' + 'bar/Foo.class', + 'bar/foo.properties', + 'bar/dep.properties' ]) and: doesNotContain(output, [ - 'foo/Foo.class', - 'foo/foo.properties', - 'foo/dep.properties' + 'foo/Foo.class', + 'foo/foo.properties', + 'foo/dep.properties' ]) } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index 8a9f640b6..742229ef3 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -63,10 +63,10 @@ class ShadowPluginSpec extends PluginSpecification { def 'Compatible with Gradle #version'() { given: File one = buildJar('one.jar').insertFile('META-INF/services/shadow.Shadow', - 'one # NOTE: No newline terminates this line/file').write() + 'one # NOTE: No newline terminates this line/file').write() repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', - 'two # NOTE: No newline terminates this line/file').publish() + 'two # NOTE: No newline terminates this line/file').publish() buildFile << """ dependencies { @@ -200,9 +200,9 @@ class ShadowPluginSpec extends PluginSpecification { then: serverOutput.exists() contains(serverOutput, [ - 'client/Client.class', - 'server/Server.class', - 'junit/framework/Test.class' + 'client/Client.class', + 'server/Server.class', + 'junit/framework/Test.class' ]) } @@ -257,8 +257,8 @@ class ShadowPluginSpec extends PluginSpecification { then: serverOutput.exists() contains(serverOutput, [ - 'client/Client.class', - 'server/Server.class' + 'client/Client.class', + 'server/Server.class' ]) doesNotContain(serverOutput, ['junit/framework/Test.class']) } @@ -312,8 +312,8 @@ class ShadowPluginSpec extends PluginSpecification { then: serverOutput.exists() contains(serverOutput, [ - 'server/Server.class', - 'junit/framework/Test.class' + 'server/Server.class', + 'junit/framework/Test.class' ]) doesNotContain(serverOutput, ['client/Client.class']) } @@ -364,8 +364,8 @@ class ShadowPluginSpec extends PluginSpecification { then: contains(serverOutput, [ - 'client/Client.class', - 'server/Server.class' + 'client/Client.class', + 'server/Server.class' ]) } @@ -419,9 +419,9 @@ class ShadowPluginSpec extends PluginSpecification { then: contains(serverOutput, [ - 'client/Client.class', - 'server/Server.class', - 'junit/framework/TestCase.class' + 'client/Client.class', + 'server/Server.class', + 'junit/framework/TestCase.class' ]) } @@ -472,9 +472,9 @@ class ShadowPluginSpec extends PluginSpecification { then: contains(serverOutput, [ - 'client/Client.class', - 'server/Server.class', - 'junit/framework/TestCase.class' + 'client/Client.class', + 'server/Server.class', + 'junit/framework/TestCase.class' ]) } @@ -551,10 +551,10 @@ class ShadowPluginSpec extends PluginSpecification { then: serverOutput.exists() contains(serverOutput, [ - 'impl/SimpleEntity.class', - 'api/Entity.class', - 'api/UnusedEntity.class', - 'lib/LibEntity.class', + 'impl/SimpleEntity.class', + 'api/Entity.class', + 'api/UnusedEntity.class', + 'lib/LibEntity.class', ]) doesNotContain(serverOutput, ['junit/framework/Test.class', 'lib/UnusedLibEntity.class']) } @@ -627,11 +627,11 @@ class ShadowPluginSpec extends PluginSpecification { then: serverOutput.exists() contains(serverOutput, [ - 'impl/SimpleEntity.class', - 'api/Entity.class', - 'api/UnusedEntity.class', - 'lib/LibEntity.class', - 'lib/UnusedLibEntity.class' + 'impl/SimpleEntity.class', + 'api/Entity.class', + 'api/UnusedEntity.class', + 'lib/LibEntity.class', + 'lib/UnusedLibEntity.class' ]) } @@ -681,14 +681,14 @@ class ShadowPluginSpec extends PluginSpecification { then: serverOutput.exists() contains(serverOutput, [ - 'server/Server.class' + 'server/Server.class' ]) and: doesNotContain(serverOutput, [ - 'client/Client.class', - 'junit/framework/Test.class', - 'client/junit/framework/Test.class' + 'client/Client.class', + 'junit/framework/Test.class', + 'client/junit/framework/Test.class' ]) } @@ -739,27 +739,27 @@ class ShadowPluginSpec extends PluginSpecification { then: serverOutput.exists() contains(serverOutput, [ - 'client/Client.class', - 'client/junit/framework/Test.class', - 'server/Server.class', + 'client/Client.class', + 'client/junit/framework/Test.class', + 'server/Server.class', ]) and: doesNotContain(serverOutput, [ - 'junit/framework/Test.class' + 'junit/framework/Test.class' ]) } def "exclude INDEX.LIST, *.SF, *.DSA, and *.RSA by default"() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('META-INF/INDEX.LIST', 'JarIndex-Version: 1.0') - .insertFile('META-INF/a.SF', 'Signature File') - .insertFile('META-INF/a.DSA', 'DSA Signature Block') - .insertFile('META-INF/a.RSA', 'RSA Signature Block') - .insertFile('META-INF/a.properties', 'key=value') - .publish() + .insertFile('a.properties', 'a') + .insertFile('META-INF/INDEX.LIST', 'JarIndex-Version: 1.0') + .insertFile('META-INF/a.SF', 'Signature File') + .insertFile('META-INF/a.DSA', 'DSA Signature Block') + .insertFile('META-INF/a.RSA', 'RSA Signature Block') + .insertFile('META-INF/a.properties', 'key=value') + .publish() file('src/main/java/shadow/Passed.java') << ''' package shadow; @@ -783,12 +783,12 @@ class ShadowPluginSpec extends PluginSpecification { def "include runtime configuration by default"() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .publish() + .insertFile('a.properties', 'a') + .publish() repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() + .insertFile('b.properties', 'b') + .publish() buildFile << """ dependencies { @@ -810,21 +810,21 @@ class ShadowPluginSpec extends PluginSpecification { def "include java-library configurations by default"() { given: repo.module('shadow', 'api', '1.0') - .insertFile('api.properties', 'api') - .publish() + .insertFile('api.properties', 'api') + .publish() repo.module('shadow', 'implementation-dep', '1.0') - .insertFile('implementation-dep.properties', 'implementation-dep') - .publish() + .insertFile('implementation-dep.properties', 'implementation-dep') + .publish() repo.module('shadow', 'implementation', '1.0') - .insertFile('implementation.properties', 'implementation') - .dependsOn('implementation-dep') - .publish() + .insertFile('implementation.properties', 'implementation') + .dependsOn('implementation-dep') + .publish() repo.module('shadow', 'runtimeOnly', '1.0') - .insertFile('runtimeOnly.properties', 'runtimeOnly') - .publish() + .insertFile('runtimeOnly.properties', 'runtimeOnly') + .publish() buildFile.text = getDefaultBuildScript('java-library') buildFile << """ @@ -846,12 +846,12 @@ class ShadowPluginSpec extends PluginSpecification { def "doesn't include compileOnly configuration by default"() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .publish() + .insertFile('a.properties', 'a') + .publish() repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() + .insertFile('b.properties', 'b') + .publish() buildFile << """ dependencies { @@ -873,12 +873,12 @@ class ShadowPluginSpec extends PluginSpecification { def "default copying strategy"() { given: repo.module('shadow', 'a', '1.0') - .insertFile('META-INF/MANIFEST.MF', 'MANIFEST A') - .publish() + .insertFile('META-INF/MANIFEST.MF', 'MANIFEST A') + .publish() repo.module('shadow', 'b', '1.0') - .insertFile('META-INF/MANIFEST.MF', 'MANIFEST B') - .publish() + .insertFile('META-INF/MANIFEST.MF', 'MANIFEST B') + .publish() buildFile << """ dependencies { @@ -1037,7 +1037,7 @@ class ShadowPluginSpec extends PluginSpecification { then: serverOutput.exists() contains(serverOutput, [ - 'api/UnusedEntity.class', + 'api/UnusedEntity.class', ]) } @@ -1046,9 +1046,9 @@ class ShadowPluginSpec extends PluginSpecification { def "check large zip files with zip64 enabled"() { given: repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() + .insertFile('a.properties', 'a') + .insertFile('a2.properties', 'a2') + .publish() file('src/main/java/myapp/Main.java') << """ package myapp; diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy index 2835108e8..f9eb12b58 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy @@ -16,16 +16,16 @@ class TransformerSpec extends PluginSpecification { def 'service resource transformer'() { given: File one = buildJar('one.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', - 'one # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'one') - .write() + .insertFile('META-INF/services/org.apache.maven.Shade', + 'one # NOTE: No newline terminates this line/file') + .insertFile('META-INF/services/com.acme.Foo', 'one') + .write() File two = buildJar('two.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', - 'two # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'two') - .write() + .insertFile('META-INF/services/org.apache.maven.Shade', + 'two # NOTE: No newline terminates this line/file') + .insertFile('META-INF/services/com.acme.Foo', 'two') + .write() buildFile << """ import ${ServiceFileTransformer.name} @@ -50,7 +50,7 @@ class TransformerSpec extends PluginSpecification { String text1 = getJarFileContents(output, 'META-INF/services/org.apache.maven.Shade') assert text1.split("\\r?\\n").size() == 2 assert text1 == -'''one # NOTE: No newline terminates this line/file + '''one # NOTE: No newline terminates this line/file two # NOTE: No newline terminates this line/file'''.stripIndent() and: @@ -61,13 +61,13 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'service resource transformer alternate path'() { given: - File one = buildJar('one.jar').insertFile('META-INF/foo/org.apache.maven.Shade', - 'one # NOTE: No newline terminates this line/file').write() + File one = buildJar('one.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('META-INF/foo/org.apache.maven.Shade', - 'two # NOTE: No newline terminates this line/file').write() + File two = buildJar('two.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + 'two # NOTE: No newline terminates this line/file').write() - buildFile << """ + buildFile << """ import ${ServiceFileTransformer.name} tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { from('${escapedPath(one)}') @@ -81,32 +81,32 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() """.stripIndent() when: - run('shadowJar') + run('shadowJar') then: - assert output.exists() + assert output.exists() and: - String text = getJarFileContents(output, 'META-INF/foo/org.apache.maven.Shade') - assert text.split("\\r?\\n").size() == 2 - assert text == -'''one # NOTE: No newline terminates this line/file + String text = getJarFileContents(output, 'META-INF/foo/org.apache.maven.Shade') + assert text.split("\\r?\\n").size() == 2 + assert text == + '''one # NOTE: No newline terminates this line/file two # NOTE: No newline terminates this line/file'''.stripIndent() } def 'service resource transformer short syntax'() { given: - File one = buildJar('one.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', - 'one # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'one') - .write() - - File two = buildJar('two.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', - 'two # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'two') - .write() + File one = buildJar('one.jar') + .insertFile('META-INF/services/org.apache.maven.Shade', + 'one # NOTE: No newline terminates this line/file') + .insertFile('META-INF/services/com.acme.Foo', 'one') + .write() + + File two = buildJar('two.jar') + .insertFile('META-INF/services/org.apache.maven.Shade', + 'two # NOTE: No newline terminates this line/file') + .insertFile('META-INF/services/com.acme.Foo', 'two') + .write() buildFile << """ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -127,39 +127,39 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() assert output.exists() and: - String text1 = getJarFileContents(output, 'META-INF/services/org.apache.maven.Shade') - assert text1.split("\\r?\\n").size() == 2 - assert text1 == -'''one # NOTE: No newline terminates this line/file + String text1 = getJarFileContents(output, 'META-INF/services/org.apache.maven.Shade') + assert text1.split("\\r?\\n").size() == 2 + assert text1 == + '''one # NOTE: No newline terminates this line/file two # NOTE: No newline terminates this line/file'''.stripIndent() and: - String text2 = getJarFileContents(output, 'META-INF/services/com.acme.Foo') - assert text2.split("\\r?\\n").size() == 1 - assert text2 == 'one' + String text2 = getJarFileContents(output, 'META-INF/services/com.acme.Foo') + assert text2.split("\\r?\\n").size() == 1 + assert text2 == 'one' } def 'service resource transformer short syntax relocation'() { given: File one = buildJar('one.jar') - .insertFile('META-INF/services/java.sql.Driver', -'''oracle.jdbc.OracleDriver + .insertFile('META-INF/services/java.sql.Driver', + '''oracle.jdbc.OracleDriver org.apache.hive.jdbc.HiveDriver'''.stripIndent()) - .insertFile('META-INF/services/org.apache.axis.components.compiler.Compiler', + .insertFile('META-INF/services/org.apache.axis.components.compiler.Compiler', 'org.apache.axis.components.compiler.Javac') - .insertFile('META-INF/services/org.apache.commons.logging.LogFactory', + .insertFile('META-INF/services/org.apache.commons.logging.LogFactory', 'org.apache.commons.logging.impl.LogFactoryImpl') - .write() + .write() File two = buildJar('two.jar') - .insertFile('META-INF/services/java.sql.Driver', -'''org.apache.derby.jdbc.AutoloadedDriver + .insertFile('META-INF/services/java.sql.Driver', + '''org.apache.derby.jdbc.AutoloadedDriver com.mysql.jdbc.Driver'''.stripIndent()) - .insertFile('META-INF/services/org.apache.axis.components.compiler.Compiler', + .insertFile('META-INF/services/org.apache.axis.components.compiler.Compiler', 'org.apache.axis.components.compiler.Jikes') - .insertFile('META-INF/services/org.apache.commons.logging.LogFactory', + .insertFile('META-INF/services/org.apache.commons.logging.LogFactory', 'org.mortbay.log.Factory') - .write() + .write() buildFile << """ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -185,7 +185,7 @@ com.mysql.jdbc.Driver'''.stripIndent()) String text1 = getJarFileContents(output, 'META-INF/services/java.sql.Driver') assert text1.split("\\r?\\n").size() == 4 assert text1 == -'''oracle.jdbc.OracleDriver + '''oracle.jdbc.OracleDriver myapache.hive.jdbc.HiveDriver myapache.derby.jdbc.AutoloadedDriver com.mysql.jdbc.Driver'''.stripIndent() @@ -194,26 +194,26 @@ com.mysql.jdbc.Driver'''.stripIndent() String text2 = getJarFileContents(output, 'META-INF/services/myapache.axis.components.compiler.Compiler') assert text2.split("\\r?\\n").size() == 2 assert text2 == -'''myapache.axis.components.compiler.Javac + '''myapache.axis.components.compiler.Javac org.apache.axis.components.compiler.Jikes'''.stripIndent() and: String text3 = getJarFileContents(output, 'META-INF/services/org.apache.commons.logging.LogFactory') assert text3.split("\\r?\\n").size() == 2 assert text3 == -'''myapache.commons.logging.impl.LogFactoryImpl + '''myapache.commons.logging.impl.LogFactoryImpl org.mortbay.log.Factory'''.stripIndent() } def 'service resource transformer short syntax alternate path'() { given: - File one = buildJar('one.jar').insertFile('META-INF/foo/org.apache.maven.Shade', - 'one # NOTE: No newline terminates this line/file').write() + File one = buildJar('one.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('META-INF/foo/org.apache.maven.Shade', - 'two # NOTE: No newline terminates this line/file').write() + File two = buildJar('two.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + 'two # NOTE: No newline terminates this line/file').write() - buildFile << """ + buildFile << """ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { from('${escapedPath(one)}') from('${escapedPath(two)}') @@ -224,16 +224,16 @@ org.mortbay.log.Factory'''.stripIndent() """.stripIndent() when: - run('shadowJar') + run('shadowJar') then: - assert output.exists() + assert output.exists() and: - String text = getJarFileContents(output, 'META-INF/foo/org.apache.maven.Shade') - assert text.split("\\r?\\n").size() == 2 - assert text == -'''one # NOTE: No newline terminates this line/file + String text = getJarFileContents(output, 'META-INF/foo/org.apache.maven.Shade') + assert text.split("\\r?\\n").size() == 2 + assert text == + '''one # NOTE: No newline terminates this line/file two # NOTE: No newline terminates this line/file'''.stripIndent() } @@ -241,24 +241,24 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'apply transformers to project resources'() { given: File one = buildJar('one.jar').insertFile('META-INF/services/shadow.Shadow', - 'one # NOTE: No newline terminates this line/file').write() + 'one # NOTE: No newline terminates this line/file').write() repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', - 'two # NOTE: No newline terminates this line/file').publish() + 'two # NOTE: No newline terminates this line/file').publish() buildFile << """ dependencies { implementation 'shadow:two:1.0' implementation files('${escapedPath(one)}') } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { mergeServiceFiles() } """.stripIndent() file('src/main/resources/META-INF/services/shadow.Shadow') << - 'three # NOTE: No newline terminates this line/file' + 'three # NOTE: No newline terminates this line/file' when: run('shadowJar') @@ -270,7 +270,7 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() String text = getJarFileContents(output, 'META-INF/services/shadow.Shadow') assert text.split("\\r?\\n").size() == 3 assert text == -'''three # NOTE: No newline terminates this line/file + '''three # NOTE: No newline terminates this line/file one # NOTE: No newline terminates this line/file two # NOTE: No newline terminates this line/file'''.stripIndent() } @@ -278,10 +278,10 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'appending transformer'() { given: File one = buildJar('one.jar').insertFile('test.properties', - 'one # NOTE: No newline terminates this line/file').write() + 'one # NOTE: No newline terminates this line/file').write() File two = buildJar('two.jar').insertFile('test.properties', - 'two # NOTE: No newline terminates this line/file').write() + 'two # NOTE: No newline terminates this line/file').write() buildFile << """ import ${AppendingTransformer.name} @@ -306,7 +306,7 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() String text = getJarFileContents(output, 'test.properties') assert text.split("\\r?\\n").size() == 2 assert text == -'''one # NOTE: No newline terminates this line/file + '''one # NOTE: No newline terminates this line/file two # NOTE: No newline terminates this line/file '''.stripIndent() } @@ -314,10 +314,10 @@ two # NOTE: No newline terminates this line/file def 'appending transformer short syntax'() { given: File one = buildJar('one.jar').insertFile('test.properties', - 'one # NOTE: No newline terminates this line/file').write() + 'one # NOTE: No newline terminates this line/file').write() File two = buildJar('two.jar').insertFile('test.properties', - 'two # NOTE: No newline terminates this line/file').write() + 'two # NOTE: No newline terminates this line/file').write() buildFile << """ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -339,7 +339,7 @@ two # NOTE: No newline terminates this line/file String text = getJarFileContents(output, 'test.properties') assert text.split("\\r?\\n").size() == 2 assert text == -'''one # NOTE: No newline terminates this line/file + '''one # NOTE: No newline terminates this line/file two # NOTE: No newline terminates this line/file '''.stripIndent() } @@ -349,9 +349,9 @@ two # NOTE: No newline terminates this line/file File main = file('src/main/java/shadow/Main.java') main << ''' package shadow; - + public class Main { - + public static void main(String[] args) { } } '''.stripIndent() @@ -386,9 +386,9 @@ two # NOTE: No newline terminates this line/file File main = file('src/main/java/shadow/Main.java') main << ''' package shadow; - + public class Main { - + public static void main(String[] args) { } } '''.stripIndent() @@ -400,7 +400,7 @@ two # NOTE: No newline terminates this line/file attributes 'Test-Entry': 'FAILED' } } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { manifest { attributes 'Test-Entry': 'PASSED' @@ -429,8 +429,8 @@ two # NOTE: No newline terminates this line/file def 'append xml files'() { given: File xml1 = buildJar('xml1.jar').insertFile('properties.xml', -''' - + ''' + val1 @@ -438,8 +438,8 @@ two # NOTE: No newline terminates this line/file ).write() File xml2 = buildJar('xml2.jar').insertFile('properties.xml', -''' - + ''' + val2 @@ -470,7 +470,7 @@ two # NOTE: No newline terminates this line/file and: String text = getJarFileContents(output, 'properties.xml') assert text.replaceAll('\r\n', '\n') == -''' + ''' val1 @@ -485,9 +485,9 @@ two # NOTE: No newline terminates this line/file File main = file('src/main/java/shadow/Main.java') main << ''' package shadow; - + public class Main { - + public static void main(String[] args) { } } '''.stripIndent() @@ -499,7 +499,7 @@ two # NOTE: No newline terminates this line/file attributes 'Test-Entry': 'FAILED' } } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { manifest { attributes 'Test-Entry': 'PASSED' @@ -545,9 +545,9 @@ two # NOTE: No newline terminates this line/file File main = file('src/main/java/shadow/Main.java') main << ''' package shadow; - + public class Main { - + public static void main(String[] args) { } } '''.stripIndent() @@ -559,7 +559,7 @@ two # NOTE: No newline terminates this line/file attributes 'Test-Entry': 'FAILED' } } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { manifest { attributes 'Test-Entry': 'PASSED' @@ -601,21 +601,21 @@ two # NOTE: No newline terminates this line/file def 'Groovy extension module transformer'() { given: - def one = buildJar('one.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', -'''moduleName=foo + def one = buildJar('one.jar') + .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + '''moduleName=foo moduleVersion=1.0.5 extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() - def two = buildJar('two.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', -'''moduleName=bar + def two = buildJar('two.jar') + .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + '''moduleName=bar moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() - buildFile << """ + buildFile << """ import ${GroovyExtensionModuleTransformer.name} tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { from('${escapedPath(one)}') @@ -628,38 +628,38 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( """.stripIndent() when: - run('shadowJar') + run('shadowJar') then: - assert output.exists() + assert output.exists() and: - def text = getJarFileContents(output, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') - def props = new Properties() - props.load(new StringReader(text)) - assert props.getProperty('moduleName') == 'MergedByShadowJar' - assert props.getProperty('moduleVersion') == '1.0.0' - assert props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' - assert props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' + def text = getJarFileContents(output, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') + def props = new Properties() + props.load(new StringReader(text)) + assert props.getProperty('moduleName') == 'MergedByShadowJar' + assert props.getProperty('moduleVersion') == '1.0.0' + assert props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' + assert props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' } def 'Groovy extension module transformer works for Groovy2_5+'() { given: - def one = buildJar('one.jar') - .insertFile('META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule', -'''moduleName=foo + def one = buildJar('one.jar') + .insertFile('META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule', + '''moduleName=foo moduleVersion=1.0.5 extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() - def two = buildJar('two.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', -'''moduleName=bar + def two = buildJar('two.jar') + .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + '''moduleName=bar moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() - buildFile << """ + buildFile << """ import ${GroovyExtensionModuleTransformer.name} tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { from('${escapedPath(one)}') @@ -672,39 +672,39 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( """.stripIndent() when: - run('shadowJar') + run('shadowJar') then: - output.exists() + output.exists() and: - def text = getJarFileContents(output, 'META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule') - def props = new Properties() - props.load(new StringReader(text)) - props.getProperty('moduleName') == 'MergedByShadowJar' - props.getProperty('moduleVersion') == '1.0.0' - props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' - props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' - doesNotContain(output, ['META-INF/services/org.codehaus.groovy.runtime.ExtensionModule']) + def text = getJarFileContents(output, 'META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule') + def props = new Properties() + props.load(new StringReader(text)) + props.getProperty('moduleName') == 'MergedByShadowJar' + props.getProperty('moduleVersion') == '1.0.0' + props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' + props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' + doesNotContain(output, ['META-INF/services/org.codehaus.groovy.runtime.ExtensionModule']) } def 'Groovy extension module transformer short syntax'() { given: - def one = buildJar('one.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', -'''moduleName=foo + def one = buildJar('one.jar') + .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + '''moduleName=foo moduleVersion=1.0.5 extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() - def two = buildJar('two.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', -'''moduleName=bar + def two = buildJar('two.jar') + .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + '''moduleName=bar moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() - buildFile << """ + buildFile << """ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { from('${escapedPath(one)}') from('${escapedPath(two)}') @@ -715,19 +715,19 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( """.stripIndent() when: - run('shadowJar') + run('shadowJar') then: - assert output.exists() + assert output.exists() and: - def text = getJarFileContents(output, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') - def props = new Properties() - props.load(new StringReader(text)) - assert props.getProperty('moduleName') == 'MergedByShadowJar' - assert props.getProperty('moduleVersion') == '1.0.0' - assert props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' - assert props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' + def text = getJarFileContents(output, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') + def props = new Properties() + props.load(new StringReader(text)) + assert props.getProperty('moduleName') == 'MergedByShadowJar' + assert props.getProperty('moduleVersion') == '1.0.0' + assert props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' + assert props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' } @Unroll diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy index bd33ca03d..72ebb78d4 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy @@ -12,7 +12,8 @@ import static org.gradle.testkit.runner.TaskOutcome.FROM_CACHE import static org.gradle.testkit.runner.TaskOutcome.SUCCESS abstract class AbstractCachingSpec extends PluginSpecification { - @TempDir Path alternateDir + @TempDir + Path alternateDir @Override def setup() { @@ -33,13 +34,13 @@ abstract class AbstractCachingSpec extends PluginSpecification { } BuildResult runWithCacheEnabled(String... arguments) { - List cacheArguments = [ '--build-cache' ] + List cacheArguments = ['--build-cache'] cacheArguments.addAll(arguments) return run(cacheArguments) } BuildResult runInAlternateDirWithCacheEnabled(String... arguments) { - List cacheArguments = [ '--build-cache' ] + List cacheArguments = ['--build-cache'] cacheArguments.addAll(arguments) // TODO: Use PluginSpecification.run here to reuse flags, but cache tests failed for now, need to investigate. return runner.withProjectDir(alternateDir.toFile()).withArguments(cacheArguments).build() diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy index a20ffdf74..47a82125d 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy @@ -45,9 +45,9 @@ class MinimizationCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'junit/framework/Test.class', - 'client/Client.class' + 'server/Server.class', + 'junit/framework/Test.class', + 'client/Client.class' ]) when: @@ -69,8 +69,8 @@ class MinimizationCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'junit/framework/Test.class' + 'server/Server.class', + 'junit/framework/Test.class' ]) doesNotContain(output, ['client/Client.class']) @@ -80,8 +80,8 @@ class MinimizationCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'junit/framework/Test.class' + 'server/Server.class', + 'junit/framework/Test.class' ]) doesNotContain(output, ['client/Client.class']) } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy index c0a75c537..9a43e7270 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy @@ -24,8 +24,8 @@ class RelocationCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'junit/framework/Test.class' + 'server/Server.class', + 'junit/framework/Test.class' ]) when: @@ -41,13 +41,13 @@ class RelocationCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/junit/framework/Test.class' + 'server/Server.class', + 'foo/junit/framework/Test.class' ]) and: doesNotContain(output, [ - 'junit/framework/Test.class' + 'junit/framework/Test.class' ]) when: @@ -56,13 +56,13 @@ class RelocationCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/junit/framework/Test.class' + 'server/Server.class', + 'foo/junit/framework/Test.class' ]) and: doesNotContain(output, [ - 'junit/framework/Test.class' + 'junit/framework/Test.class' ]) } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy index 0d6a2d741..def98418c 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy @@ -84,7 +84,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { given: buildFile << """ dependencies { implementation 'junit:junit:3.8.2' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { exclude 'junit/*' } @@ -112,8 +112,8 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'server/Util.class' + 'server/Server.class', + 'server/Util.class' ]) when: @@ -130,13 +130,13 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) and: doesNotContain(output, [ - 'server/Util.class', - 'junit/framework/Test.class' + 'server/Util.class', + 'junit/framework/Test.class' ]) when: @@ -145,13 +145,13 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) and: doesNotContain(output, [ - 'server/Util.class', - 'junit/framework/Test.class' + 'server/Util.class', + 'junit/framework/Test.class' ]) } @@ -178,8 +178,8 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'junit/framework/Test.class' + 'server/Server.class', + 'junit/framework/Test.class' ]) when: @@ -197,12 +197,12 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) and: doesNotContain(output, [ - 'junit/framework/Test.class' + 'junit/framework/Test.class' ]) when: @@ -211,12 +211,12 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) and: doesNotContain(output, [ - 'junit/framework/Test.class' + 'junit/framework/Test.class' ]) } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy index 93cf1477a..a8507c9af 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy @@ -23,26 +23,26 @@ class TransformCachingSpec extends AbstractCachingSpec { import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement - + class CustomTransformer implements Transformer { @Override boolean canTransformResource(FileTreeElement element) { return false } - + @Override void transform(TransformerContext context) { - + } - + @Override boolean hasTransformedResource() { return false } - + @Override void modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps) { - + } } @@ -58,7 +58,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) when: @@ -67,7 +67,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) } @@ -89,7 +89,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) when: @@ -106,7 +106,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) when: @@ -115,7 +115,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) when: @@ -132,7 +132,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) when: @@ -141,7 +141,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) } @@ -164,7 +164,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) when: @@ -181,8 +181,8 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/bar.properties' + 'server/Server.class', + 'foo/bar.properties' ]) when: @@ -191,8 +191,8 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/bar.properties' + 'server/Server.class', + 'foo/bar.properties' ]) when: @@ -211,8 +211,8 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/baz.properties' + 'server/Server.class', + 'foo/baz.properties' ]) when: @@ -221,8 +221,8 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/baz.properties' + 'server/Server.class', + 'foo/baz.properties' ]) } @@ -245,7 +245,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) when: @@ -262,8 +262,8 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/bar.xml' + 'server/Server.class', + 'foo/bar.xml' ]) when: @@ -272,8 +272,8 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/bar.xml' + 'server/Server.class', + 'foo/bar.xml' ]) when: @@ -292,8 +292,8 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/baz.xml' + 'server/Server.class', + 'foo/baz.xml' ]) when: @@ -302,8 +302,8 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class', - 'foo/baz.xml' + 'server/Server.class', + 'foo/baz.xml' ]) } @@ -325,7 +325,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) when: @@ -340,7 +340,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) when: @@ -349,7 +349,7 @@ class TransformCachingSpec extends AbstractCachingSpec { then: output.exists() contains(output, [ - 'server/Server.class' + 'server/Server.class' ]) } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/ManualCodeSnippetTests.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/ManualCodeSnippetTests.groovy index 76173de2e..e56470122 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/ManualCodeSnippetTests.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/ManualCodeSnippetTests.groovy @@ -16,9 +16,9 @@ import java.nio.file.Path class ManualCodeSnippetTests { public static final LinkedHashMap FIXTURES = [ - "groovy": new GradleBuildExecuter("build.gradle", new GroovyDslFixture(), new GroovyDslFixture.ImportsExtractor()), - "groovy no-plugins": new GradleBuildExecuter("build.gradle", new GroovyScriptFixture(), new GroovyDslFixture.ImportsExtractor()), - "groovy no-run": new NoopExecuter() + "groovy" : new GradleBuildExecuter("build.gradle", new GroovyDslFixture(), new GroovyDslFixture.ImportsExtractor()), + "groovy no-plugins": new GradleBuildExecuter("build.gradle", new GroovyScriptFixture(), new GroovyDslFixture.ImportsExtractor()), + "groovy no-run" : new NoopExecuter() ] @TestFactory diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/extractor/ManualSnippetExtractor.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/extractor/ManualSnippetExtractor.groovy index 99d84ca20..7f1f85aa1 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/extractor/ManualSnippetExtractor.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/extractor/ManualSnippetExtractor.groovy @@ -26,7 +26,7 @@ class ManualSnippetExtractor { private static void addSnippets(Path tempDir, List snippets, File file, Pattern snippetBlockPattern, SnippetExecuter executer) { def source = file.text - String testName = file.parentFile.name + "/" +file.name + String testName = file.parentFile.name + "/" + file.name Map snippetsByLine = findSnippetsByLine(source, snippetBlockPattern) snippetsByLine.each { lineNumber, snippet -> diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/ExceptionTransformer.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/ExceptionTransformer.groovy index cee765c2c..15331d0ef 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/ExceptionTransformer.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/ExceptionTransformer.groovy @@ -2,38 +2,38 @@ package com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.execut class ExceptionTransformer { - final String sourceClassName - final String sourceFileName - final Integer lineNumber + final String sourceClassName + final String sourceFileName + final Integer lineNumber - ExceptionTransformer(String sourceClassName, String sourceFileName, Integer lineNumber) { - this.sourceClassName = sourceClassName - this.sourceFileName = sourceFileName - this.lineNumber = lineNumber - } + ExceptionTransformer(String sourceClassName, String sourceFileName, Integer lineNumber) { + this.sourceClassName = sourceClassName + this.sourceFileName = sourceFileName + this.lineNumber = lineNumber + } - Throwable transform(Throwable throwable, Integer offset) throws Exception { - def errorLine = 0 + Throwable transform(Throwable throwable, Integer offset) throws Exception { + def errorLine = 0 - if (throwable instanceof CompileException) { - errorLine = throwable.lineNo - } else { - def frame = throwable.getStackTrace().find { it.fileName == sourceClassName } - if (frame) { - errorLine = frame.lineNumber - } else { - frame = throwable.getStackTrace().find { it.fileName == "Example.java" } - if (frame) { - errorLine = frame.lineNumber + if (throwable instanceof CompileException) { + errorLine = throwable.lineNo + } else { + def frame = throwable.getStackTrace().find { it.fileName == sourceClassName } + if (frame) { + errorLine = frame.lineNumber + } else { + frame = throwable.getStackTrace().find { it.fileName == "Example.java" } + if (frame) { + errorLine = frame.lineNumber + } + } } - } + errorLine = errorLine - offset + StackTraceElement[] stack = throwable.getStackTrace() + List newStack = new ArrayList(stack.length + 1) + newStack.add(new StackTraceElement(sourceClassName, "javadoc", sourceFileName, lineNumber + errorLine)) + newStack.addAll(stack) + throwable.setStackTrace(newStack as StackTraceElement[]) + throwable } - errorLine = errorLine - offset - StackTraceElement[] stack = throwable.getStackTrace() - List newStack = new ArrayList(stack.length + 1) - newStack.add(new StackTraceElement(sourceClassName, "javadoc", sourceFileName, lineNumber + errorLine)) - newStack.addAll(stack) - throwable.setStackTrace(newStack as StackTraceElement[]) - throwable - } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/SnippetExecuter.java b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/SnippetExecuter.java index 1276fb9ae..a988861a3 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/SnippetExecuter.java +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/SnippetExecuter.java @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer; -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixture.SnippetFixture; import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.TestCodeSnippet; +import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixture.SnippetFixture; import java.io.File; diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/GroovyScriptFixture.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/GroovyScriptFixture.groovy index deff8e9f0..04fdccd67 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/GroovyScriptFixture.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/GroovyScriptFixture.groovy @@ -2,9 +2,9 @@ package com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixtur class GroovyScriptFixture extends SnippetFixture { - @Override - String post() { - "\n;0;" - } + @Override + String post() { + "\n;0;" + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy index c757308e9..87a8ea0e2 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy @@ -21,7 +21,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import org.junit.jupiter.api.Test -import static org.junit.jupiter.api.Assertions.* +import static org.junit.jupiter.api.Assertions.fail /** diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy index 18c7911ab..ba04bfb51 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy @@ -23,7 +23,7 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowStats import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import static org.junit.jupiter.api.Assertions.* +import static org.junit.jupiter.api.Assertions.assertEquals /** * Test for {@link SimpleRelocator}. @@ -42,7 +42,7 @@ class SimpleRelocatorTest { @BeforeEach void setUp() { - stats = new ShadowStats() + stats = new ShadowStats() } @Test @@ -64,7 +64,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("/org/Foo/Class.class")) relocator = new SimpleRelocator("org.foo", null, null, Arrays.asList( - [ "org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff" ] as String[])) + ["org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff"] as String[])) assertEquals(true, relocator.canRelocatePath("org/foo/Class")) assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) assertEquals(true, relocator.canRelocatePath("org/foo/excluded")) @@ -99,8 +99,8 @@ class SimpleRelocatorTest { assertEquals(true, relocator.canRelocatePath("/org/f")) // equal to path pattern with / } - @Test - void testCanRelocatePathWithRegex() { + @Test + void testCanRelocatePathWithRegex() { SimpleRelocator relocator // Include with Regex @@ -126,12 +126,12 @@ class SimpleRelocatorTest { // Include with Regex and normal pattern relocator = new SimpleRelocator("org.foo", null, - Arrays.asList("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*"), null) + Arrays.asList("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*"), null) assertEquals(true, relocator.canRelocatePath("org/foo/Factory1.class")) assertEquals(true, relocator.canRelocatePath("org/foo/public/Bar.class")) assertEquals(false, relocator.canRelocatePath("org/foo/Factory.class")) assertEquals(false, relocator.canRelocatePath("org/foo/R.class")) - } + } @Test void testCanRelocateClass() { @@ -144,7 +144,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocateClass("org.Foo.Class")) relocator = new SimpleRelocator("org.foo", null, null, Arrays.asList( - [ "org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff" ] as String[])) + ["org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff"] as String[])) assertEquals(true, relocator.canRelocateClass("org.foo.Class")) assertEquals(true, relocator.canRelocateClass("org.foo.excluded")) assertEquals(false, relocator.canRelocateClass("org.foo.Excluded")) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy index 589f2afc3..85f59721d 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy @@ -22,7 +22,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import static org.junit.jupiter.api.Assertions.* +import static org.junit.jupiter.api.Assertions.assertFalse +import static org.junit.jupiter.api.Assertions.assertTrue /** * Test for {@link ApacheLicenseResourceTransformer}. diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy index d273ea236..e81f6ab8b 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy @@ -24,7 +24,8 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import static org.junit.jupiter.api.Assertions.* +import static org.junit.jupiter.api.Assertions.assertTrue +import static org.junit.jupiter.api.Assertions.fail /** * Tests {@link ApacheLicenseResourceTransformer} parameters. @@ -44,11 +45,11 @@ class ApacheNoticeResourceTransformerParameterTests extends TransformerTestSuppo @Test void testCanTransformResource() { - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.TXT"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.txt"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.md"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.md"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.TXT"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.txt"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.md"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.md"))) } @Test diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy index 65778420c..0b2646aa8 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy @@ -22,7 +22,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import static org.junit.jupiter.api.Assertions.* +import static org.junit.jupiter.api.Assertions.assertFalse +import static org.junit.jupiter.api.Assertions.assertTrue /** * Test for {@link ApacheNoticeResourceTransformer}. diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy index 3797bc85a..dc18c3611 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy @@ -22,7 +22,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import static org.junit.jupiter.api.Assertions.* +import static org.junit.jupiter.api.Assertions.assertFalse +import static org.junit.jupiter.api.Assertions.assertTrue /** * Test for {@link AppendingTransformer}. diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy index a6e04d92d..edf9ea9e1 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy @@ -20,12 +20,11 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.ShadowStats - +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import org.codehaus.plexus.util.IOUtil import org.custommonkey.xmlunit.Diff import org.custommonkey.xmlunit.XMLAssert import org.custommonkey.xmlunit.XMLUnit -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import org.codehaus.plexus.util.IOUtil import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -52,22 +51,22 @@ class ComponentsXmlResourceTransformerTest extends TransformerTestSupport emptyList()) - .stats(stats) - .build()) + TransformerContext.builder() + .path("components-1.xml") + .inputStream(getClass().getResourceAsStream("/components-1.xml")) + .relocators(Collections. emptyList()) + .stats(stats) + .build()) transformer.transform( - TransformerContext.builder() - .path("components-1.xml") - .inputStream(getClass().getResourceAsStream("/components-2.xml")) - .relocators(Collections. emptyList()) - .stats(stats) - .build()) + TransformerContext.builder() + .path("components-1.xml") + .inputStream(getClass().getResourceAsStream("/components-2.xml")) + .relocators(Collections. emptyList()) + .stats(stats) + .build()) Diff diff = XMLUnit.compareXML( - IOUtil.toString(getClass().getResourceAsStream("/components-expected.xml"), "UTF-8"), - IOUtil.toString(transformer.getTransformedResource(), "UTF-8")) + IOUtil.toString(getClass().getResourceAsStream("/components-expected.xml"), "UTF-8"), + IOUtil.toString(transformer.getTransformedResource(), "UTF-8")) //assertEquals( IOUtil.toString( getClass().getResourceAsStream( "/components-expected.xml" ), "UTF-8" ), // IOUtil.toString( transformer.getTransformedResource(), "UTF-8" ).replaceAll("\r\n", "\n") ) XMLAssert.assertXMLIdentical(diff, true) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy index 716fc1675..016b1968c 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy @@ -7,7 +7,6 @@ import org.apache.logging.log4j.core.config.plugins.processor.PluginCache import org.apache.tools.zip.ZipOutputStream import spock.lang.Specification - import static java.util.Collections.singletonList import static org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy index b99a4fb39..1da4856ae 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy @@ -72,7 +72,7 @@ class ManifestAppenderTransformerTest extends TransformerTestSupportemptyList(), new ShadowStats())) + transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) } def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") @@ -104,7 +104,7 @@ class ManifestAppenderTransformerTest extends TransformerTestSupportemptyList(), new ShadowStats())) + transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") def fileOutputStream = new FileOutputStream(testableZipFile) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy index 333048ac4..578d4a1df 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy @@ -158,7 +158,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { output == toMap(transformer.propertiesEntries[path]) where: - path | charset | input || output - 'utf8.properties' | 'utf-8' | ['foo': '传傳磨宿说説'] || ['foo': '传傳磨宿说説'] + path | charset | input || output + 'utf8.properties' | 'utf-8' | ['foo': '传傳磨宿说説'] || ['foo': '传傳磨宿说説'] } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy index e8298b2ef..856d55cf5 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy @@ -35,7 +35,7 @@ final class PropertiesFileTransformerTest extends TransformerTestSupportemptyList(), new ShadowStats())) + transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) def testableZipFile = doTransformAndGetTransformedFile(transformer, false) def targetLines = readFrom(testableZipFile, MANIFEST_NAME) @@ -47,7 +47,7 @@ final class PropertiesFileTransformerTest extends TransformerTestSupportemptyList(), new ShadowStats())) + transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) def firstRunTransformedFile = doTransformAndGetTransformedFile(transformer, true) def firstRunTargetLines = readFrom(firstRunTransformedFile, MANIFEST_NAME) @@ -60,7 +60,7 @@ final class PropertiesFileTransformerTest extends TransformerTestSupport tasks) { @@ -93,7 +94,7 @@ abstract class PluginSpecification extends Specification { static boolean containsDeprecationWarning(String output) { output.contains("has been deprecated and is scheduled to be removed in Gradle") || - output.contains("has been deprecated. This is scheduled to be removed in Gradle") + output.contains("has been deprecated. This is scheduled to be removed in Gradle") } File getBuildFile() { diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java index c18966cda..da2976a59 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java @@ -203,7 +203,7 @@ public void copyTo(File target) { FileUtils.copyDirectory(this, target); } catch (IOException e) { throw new RuntimeException(String.format("Could not copy test directory '%s' to '%s'", this, - target), e); + target), e); } } else { try { @@ -394,7 +394,7 @@ public TestFile createDir() { return this; } throw new AssertionError("Problems creating dir: " + this - + ". Diagnostics: exists=" + this.exists() + ", isFile=" + this.isFile() + ", isDirectory=" + this.isDirectory()); + + ". Diagnostics: exists=" + this.exists() + ", isFile=" + this.isFile() + ", isDirectory=" + this.isDirectory()); } public TestFile createDir(Object path) { @@ -408,6 +408,7 @@ public TestFile deleteDir() { /** * Attempts to delete this directory, ignoring failures to do so. + * * @return this */ public TestFile maybeDeleteDir() { @@ -444,7 +445,7 @@ public TestFile createZip(Object path) { return zipFile; } - public TestFile zipTo(TestFile zipFile){ + public TestFile zipTo(TestFile zipFile) { new TestFileHelper(this).zipTo(zipFile, useNativeTools); return this; } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy index c9c9db88f..414f09132 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy @@ -10,7 +10,7 @@ import org.apache.tools.ant.taskdefs.Zip import java.util.zip.ZipInputStream -import static org.junit.jupiter.api.Assertions.* +import static org.junit.jupiter.api.Assertions.assertTrue class TestFileHelper { TestFile file @@ -21,7 +21,7 @@ class TestFileHelper { void unzipTo(File target, boolean nativeTools) { // Check that each directory in hierarchy is present - file.withInputStream {InputStream instr -> + file.withInputStream { InputStream instr -> def dirs = [] as Set def zipStr = new ZipInputStream(instr) def entry diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy index 34ca1ff23..2af22f3e9 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy @@ -31,8 +31,7 @@ class TestWorkspaceBuilder { def methodMissing(String name, Object args) { if (args.length == 1 && args[0] instanceof Closure) { baseDir.file(name).create(args[0]) - } - else { + } else { throw new MissingMethodException(name, getClass(), args) } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy index c9aa10403..16a2fffd6 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy @@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.util.file.TestFile import com.github.jengelman.gradle.plugins.shadow.util.repo.AbstractModule import groovy.xml.MarkupBuilder import groovy.xml.XmlParser + import java.text.SimpleDateFormat abstract class AbstractMavenModule extends AbstractModule implements MavenModule { @@ -166,7 +167,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule for (name in artifactNames) { assert actual.remove(name) - if(publishesHashFiles()) { + if (publishesHashFiles()) { assert actual.remove("${name}.md5" as String) assert actual.remove("${name}.sha1" as String) } @@ -299,7 +300,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule } } else { versions { - allVersions.each {currVersion -> + allVersions.each { currVersion -> version(currVersion) } } @@ -339,5 +340,6 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule } protected abstract boolean publishesMetaDataFile() + protected abstract boolean publishesHashFiles() } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy index ab2ab217f..bccdb40f1 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy @@ -5,7 +5,7 @@ import groovy.xml.XmlParser /** * http://maven.apache.org/ref/3.0.1/maven-repository-metadata/repository-metadata.html */ -class DefaultMavenMetaData implements MavenMetaData{ +class DefaultMavenMetaData implements MavenMetaData { String text diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy index 4838f4ab8..9aa3d1ba4 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy @@ -11,7 +11,7 @@ class MavenPom { final Map scopes = [:] MavenPom(File pomFile) { - if (pomFile.exists()){ + if (pomFile.exists()) { def pom = new XmlParser().parse(pomFile) groupId = pom.groupId[0]?.text() @@ -29,11 +29,11 @@ class MavenPom { scopes[scopeName] = scope } MavenDependency mavenDependency = new MavenDependency( - groupId: dep.groupId.text(), - artifactId: dep.artifactId.text(), - version: dep.version.text(), - classifier: dep.classifier ? dep.classifier.text() : null, - type: dep.type ? dep.type.text() : null + groupId: dep.groupId.text(), + artifactId: dep.artifactId.text(), + version: dep.version.text(), + classifier: dep.classifier ? dep.classifier.text() : null, + type: dep.type ? dep.type.text() : null ) def key = "${mavenDependency.groupId}:${mavenDependency.artifactId}:${mavenDependency.version}" key += mavenDependency.classifier ? ":${mavenDependency.classifier}" : "" From 7269e493086aa36586eb59a934da7b167e135e15 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 26 Nov 2024 10:37:28 -0500 Subject: [PATCH 031/941] Simplify SimpleRelocator new instances (#1048) --- .../SimpleRelocatorParameterTest.groovy | 2 +- .../relocation/SimpleRelocatorTest.groovy | 28 +++++++++---------- ...g4j2PluginsCacheFileTransformerSpec.groovy | 4 +-- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy index 87a8ea0e2..9e47404ca 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy @@ -44,7 +44,7 @@ class SimpleRelocatorParameterTest { private static void constructThenFailOnNullPointerException(String pattern, String shadedPattern) { try { - new SimpleRelocator(pattern, shadedPattern, Collections. emptyList(), Collections. emptyList()) + new SimpleRelocator(pattern, shadedPattern) } catch (NullPointerException ignored) { fail("Constructor should not throw null pointer exceptions") diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy index ba04bfb51..f1488c523 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy @@ -49,7 +49,7 @@ class SimpleRelocatorTest { void testCanRelocatePath() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null, null, null) + relocator = new SimpleRelocator("org.foo", null) assertEquals(true, relocator.canRelocatePath("org/foo/Class")) assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class")) @@ -63,8 +63,8 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("/org/Foo/Class")) assertEquals(false, relocator.canRelocatePath("/org/Foo/Class.class")) - relocator = new SimpleRelocator("org.foo", null, null, Arrays.asList( - ["org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff"] as String[])) + relocator = new SimpleRelocator("org.foo", null, null, + List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) assertEquals(true, relocator.canRelocatePath("org/foo/Class")) assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) assertEquals(true, relocator.canRelocatePath("org/foo/excluded")) @@ -90,7 +90,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class.class")) // Verify edge cases - relocator = new SimpleRelocator("org.f", null, null, null) + relocator = new SimpleRelocator("org.f", null) assertEquals(false, relocator.canRelocatePath("")) // Empty path assertEquals(false, relocator.canRelocatePath(".class")) // only .class assertEquals(false, relocator.canRelocatePath("te")) // shorter than path pattern @@ -115,7 +115,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/R\$string.class")) // Exclude with Regex - relocator = new SimpleRelocator("org.foo", null, null, null) + relocator = new SimpleRelocator("org.foo", null) relocator.exclude("%regex[org/foo/.*Factory[0-9].*]") assertEquals(true, relocator.canRelocatePath("org/foo/Factory.class")) assertEquals(true, relocator.canRelocatePath("org/foo/FooFactoryMain.class")) @@ -126,7 +126,7 @@ class SimpleRelocatorTest { // Include with Regex and normal pattern relocator = new SimpleRelocator("org.foo", null, - Arrays.asList("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*"), null) + List.of("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*")) assertEquals(true, relocator.canRelocatePath("org/foo/Factory1.class")) assertEquals(true, relocator.canRelocatePath("org/foo/public/Bar.class")) assertEquals(false, relocator.canRelocatePath("org/foo/Factory.class")) @@ -137,14 +137,14 @@ class SimpleRelocatorTest { void testCanRelocateClass() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null, null, null) + relocator = new SimpleRelocator("org.foo", null) assertEquals(true, relocator.canRelocateClass("org.foo.Class")) assertEquals(true, relocator.canRelocateClass("org.foo.bar.Class")) assertEquals(false, relocator.canRelocateClass("com.foo.bar.Class")) assertEquals(false, relocator.canRelocateClass("org.Foo.Class")) - relocator = new SimpleRelocator("org.foo", null, null, Arrays.asList( - ["org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff"] as String[])) + relocator = new SimpleRelocator("org.foo", null, null, + List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) assertEquals(true, relocator.canRelocateClass("org.foo.Class")) assertEquals(true, relocator.canRelocateClass("org.foo.excluded")) assertEquals(false, relocator.canRelocateClass("org.foo.Excluded")) @@ -176,7 +176,7 @@ class SimpleRelocatorTest { //MSHADE-119, make sure that the easy part of this works. @Test void testCanRelocateAbsClassPath() { - SimpleRelocator relocator = new SimpleRelocator("org.apache.velocity", "org.apache.momentum", null, null) + SimpleRelocator relocator = new SimpleRelocator("org.apache.velocity", "org.apache.momentum") assertEquals("/org/apache/momentum/mass.properties", relocator.relocatePath(pathContext("/org/apache/velocity/mass.properties"))) } @@ -185,10 +185,10 @@ class SimpleRelocatorTest { void testRelocatePath() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null, null, null) + relocator = new SimpleRelocator("org.foo", null) assertEquals("hidden/org/foo/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) - relocator = new SimpleRelocator("org.foo", "private.stuff", null, null) + relocator = new SimpleRelocator("org.foo", "private.stuff") assertEquals("private/stuff/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) } @@ -196,10 +196,10 @@ class SimpleRelocatorTest { void testRelocateClass() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null, null, null) + relocator = new SimpleRelocator("org.foo", null) assertEquals("hidden.org.foo.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) - relocator = new SimpleRelocator("org.foo", "private.stuff", null, null) + relocator = new SimpleRelocator("org.foo", "private.stuff") assertEquals("private.stuff.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy index 016b1968c..e48aaf66a 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy @@ -35,7 +35,7 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { void "should transform"() { given: List relocators = new ArrayList<>() - relocators.add(new SimpleRelocator(null, null, null, null)) + relocators.add(new SimpleRelocator(null, null)) when: transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators)) @@ -49,7 +49,7 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { String pattern = "org.apache.logging" String destination = "new.location.org.apache.logging" - List relocators = singletonList((Relocator) new SimpleRelocator(pattern, destination, null, null)) + List relocators = singletonList((Relocator) new SimpleRelocator(pattern, destination)) when: transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators, new ShadowStats())) From 932f8f9f246f5344b1222cb40b29be135f906c36 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 26 Nov 2024 10:52:58 -0500 Subject: [PATCH 032/941] Tweak imports in docs (#1049) --- src/docs/configuration/relocation/README.md | 2 -- src/docs/custom-tasks/README.md | 6 ++---- src/docs/plugins/README.md | 2 -- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/docs/configuration/relocation/README.md b/src/docs/configuration/relocation/README.md index cb37740d8..f28ca7972 100644 --- a/src/docs/configuration/relocation/README.md +++ b/src/docs/configuration/relocation/README.md @@ -71,8 +71,6 @@ To configure automatic dependency relocation, set `enableRelocation = true` and ```groovy // Configure Auto Relocation -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { enableRelocation = true relocationPrefix = "myapp" diff --git a/src/docs/custom-tasks/README.md b/src/docs/custom-tasks/README.md index ce43c789c..2fd693a4b 100644 --- a/src/docs/custom-tasks/README.md +++ b/src/docs/custom-tasks/README.md @@ -7,9 +7,7 @@ dependencies to merge into the output. ```groovy // Shadowing Test Sources and Dependencies -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar - -task testJar(type: ShadowJar) { +task testJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { archiveClassifier.set("tests") from sourceSets.test.output configurations = [project.configurations.testRuntimeClasspath] @@ -19,4 +17,4 @@ task testJar(type: ShadowJar) { The code snippet above will generate a shadowed JAR containing both the `main` and `test` sources as well as all `testRuntimeOnly` and `testImplementation` dependencies. The file is output to `build/libs/--tests.jar`. - \ No newline at end of file + diff --git a/src/docs/plugins/README.md b/src/docs/plugins/README.md index ab08748f8..81bccc274 100644 --- a/src/docs/plugins/README.md +++ b/src/docs/plugins/README.md @@ -11,8 +11,6 @@ and `relocationPrefix` settings on any `ShadowJar` task. A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the `shadowJar` task for relocation. ```groovy no-plugins -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar - plugins { id 'com.gradleup.shadow' version '@version@' id 'java' From 7bd3c3fc653045286fade4edc37ab47cfe6360a2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 27 Nov 2024 00:02:10 -0500 Subject: [PATCH 033/941] Migrate transformers to using lazy properties (#1036) * Inject ObjectFactory for Transformer * Migrate AppendingTransformer * Add ObjectFactory.property extension * Migrate ApacheNoticeResourceTransformer * Migrate DontIncludeResourceTransformer * Migrate IncludeResourceTransformer * Migrate ManifestAppenderTransformer * Migrate ManifestResourceTransformer * Migrate XmlAppendingTransformer * Migrate PropertiesFileTransformer * Replace === with == * Mark all public properties open * Migrate MergeStrategy to enum * Migrate charset properties to Charsets * Reformat * Ensure charset must be serializable * Note this change * Mark objectFactory properties final * Tweak objectFactory * Tweak kdoc for PropertiesFileTransformer * Inline getters * Replace values with entries * IncludeResourceTransformer.file is not optional * Rename encoding to charsetName * Migrate PropertiesFileTransformer.mappings to using Properties * Tweak comment * Revert "Migrate PropertiesFileTransformer.mappings to using Properties" This reverts commit 32da3ee8f5f307839e24aa77813a02a6cab90703. * Revert annoying changes to reduce diffs * Tweak style --- api/shadow.api | 116 ++++++++--------- src/docs/changes/README.md | 1 + .../gradle/plugins/shadow/internal/Utils.kt | 8 ++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 13 +- .../ApacheNoticeResourceTransformer.kt | 59 +++++---- .../transformers/AppendingTransformer.kt | 14 ++- .../DontIncludeResourceTransformer.kt | 12 +- .../IncludeResourceTransformer.kt | 20 +-- .../ManifestAppenderTransformer.kt | 22 ++-- .../ManifestResourceTransformer.kt | 23 ++-- .../transformers/PropertiesFileTransformer.kt | 117 ++++++++++++------ .../shadow/transformers/Transformer.kt | 10 ++ .../transformers/XmlAppendingTransformer.kt | 18 ++- .../plugins/shadow/TransformerSpec.groovy | 2 +- ...ceResourceTransformerParameterTests.groovy | 2 +- ...ApacheNoticeResourceTransformerTest.groovy | 2 +- .../AppendingTransformerTest.groovy | 4 +- .../ManifestAppenderTransformerTest.groovy | 2 +- .../PropertiesFileTransformerSpec.groovy | 31 ++--- .../PropertiesFileTransformerTest.groovy | 2 +- .../TransformerSpecSupport.groovy | 3 + .../TransformerTestSupport.groovy | 2 + .../XmlAppendingTransformerTest.groovy | 4 +- 23 files changed, 305 insertions(+), 182 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 2d4ff90ba..8b962c1e4 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -363,46 +363,38 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicen public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getName ()Ljava/lang/String; + public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public fun ()V + public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public final fun getAddHeader ()Z - public final fun getCopyright ()Ljava/lang/String; - public final fun getEncoding ()Ljava/lang/String; - public final fun getInceptionYear ()Ljava/lang/String; - public final fun getOrganizationName ()Ljava/lang/String; - public final fun getOrganizationURL ()Ljava/lang/String; - public final fun getPreamble1 ()Ljava/lang/String; - public final fun getPreamble2 ()Ljava/lang/String; - public final fun getPreamble3 ()Ljava/lang/String; - public final fun getProjectName ()Ljava/lang/String; + public fun getAddHeader ()Lorg/gradle/api/provider/Property; + public fun getCharsetName ()Lorg/gradle/api/provider/Property; + public fun getCopyright ()Lorg/gradle/api/provider/Property; + public fun getInceptionYear ()Lorg/gradle/api/provider/Property; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public fun getOrganizationName ()Lorg/gradle/api/provider/Property; + public fun getOrganizationURL ()Lorg/gradle/api/provider/Property; + public fun getPreamble1 ()Lorg/gradle/api/provider/Property; + public fun getPreamble2 ()Lorg/gradle/api/provider/Property; + public fun getPreamble3 ()Lorg/gradle/api/provider/Property; + public fun getProjectName ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public final fun setAddHeader (Z)V - public final fun setCopyright (Ljava/lang/String;)V - public final fun setEncoding (Ljava/lang/String;)V - public final fun setInceptionYear (Ljava/lang/String;)V - public final fun setOrganizationName (Ljava/lang/String;)V - public final fun setOrganizationURL (Ljava/lang/String;)V - public final fun setPreamble1 (Ljava/lang/String;)V - public final fun setPreamble2 (Ljava/lang/String;)V - public final fun setPreamble3 (Ljava/lang/String;)V - public final fun setProjectName (Ljava/lang/String;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } public class com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public fun ()V + public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public final fun getResource ()Ljava/lang/String; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public fun getResource ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public final fun setResource (Ljava/lang/String;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } @@ -423,13 +415,13 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Compo } public class com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public fun ()V + public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getName ()Ljava/lang/String; - public final fun getResource ()Ljava/lang/String; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public fun getResource ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public final fun setResource (Ljava/lang/String;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } @@ -442,15 +434,14 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExten } public class com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public fun ()V + public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public final fun getFile ()Ljava/io/File; + public fun getFile ()Lorg/gradle/api/file/RegularFileProperty; public fun getName ()Ljava/lang/String; - public final fun getResource ()Ljava/lang/String; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public fun getResource ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public final fun setFile (Ljava/io/File;)V - public final fun setResource (Ljava/lang/String;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } @@ -463,25 +454,25 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2Plugi } public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public fun ()V + public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun append (Ljava/lang/String;Ljava/lang/Comparable;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer; public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public fun getAttributes ()Ljava/util/List; + public fun getAttributes ()Lorg/gradle/api/provider/ListProperty; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public fun ()V + public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun attributes (Ljava/util/Map;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer; public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public final fun getMainClass ()Ljava/lang/String; - public final fun getManifestEntries ()Ljava/util/Map; + public fun getMainClass ()Lorg/gradle/api/provider/Property; + public fun getManifestEntries ()Lorg/gradle/api/provider/MapProperty; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public final fun setMainClass (Ljava/lang/String;)V - public final fun setManifestEntries (Ljava/util/Map;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } @@ -494,25 +485,35 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/NoOpT } public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public fun ()V + public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public final fun getCharset ()Ljava/lang/String; - public final fun getKeyTransformer ()Ljava/util/function/Function; - public final fun getMappings ()Ljava/util/Map; - public final fun getMergeSeparator ()Ljava/lang/String; - public final fun getMergeStrategy ()Ljava/lang/String; - public final fun getPaths ()Ljava/util/List; + public fun getCharsetName ()Lorg/gradle/api/provider/Property; + public fun getKeyTransformer ()Lorg/gradle/api/provider/Property; + public fun getMappings ()Lorg/gradle/api/provider/MapProperty; + public fun getMergeSeparator ()Lorg/gradle/api/provider/Property; + public fun getMergeStrategy ()Lorg/gradle/api/provider/Property; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public fun getPaths ()Lorg/gradle/api/provider/ListProperty; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public final fun setCharset (Ljava/lang/String;)V - public final fun setKeyTransformer (Ljava/util/function/Function;)V - public final fun setMappings (Ljava/util/Map;)V - public final fun setMergeSeparator (Ljava/lang/String;)V - public final fun setMergeStrategy (Ljava/lang/String;)V - public final fun setPaths (Ljava/util/List;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } +public final class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy : java/lang/Enum { + public static final field Append Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy$Companion; + public static final field First Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; + public static final field Latest Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; + public static final fun from (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; + public static fun values ()[Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy$Companion { + public final fun from (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; +} + public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer, org/gradle/api/tasks/util/PatternFilterable { public fun ()V public fun (Lorg/gradle/api/tasks/util/PatternSet;)V @@ -545,6 +546,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFile public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer : org/gradle/api/Named { public abstract fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getName ()Ljava/lang/String; + public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public abstract fun hasTransformedResource ()Z public abstract fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V @@ -552,6 +554,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/trans public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$DefaultImpls { public static fun getName (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Ljava/lang/String; + public static fun getObjectFactory (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lorg/gradle/api/model/ObjectFactory; } public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext { @@ -594,14 +597,13 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans public class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer$Companion; public static final field XSI_NS Ljava/lang/String; - public fun ()V + public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public final fun getIgnoreDtd ()Z - public final fun getResource ()Ljava/lang/String; + public fun getIgnoreDtd ()Lorg/gradle/api/provider/Property; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public fun getResource ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public final fun setIgnoreDtd (Z)V - public final fun setResource (Ljava/lang/String;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 389673961..6b159a93f 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -10,6 +10,7 @@ **Changed** - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) +- **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) - **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) `isEnableRelocation` is deprecated, use `enableRelocation` instead. - **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 8e4128b8d..2250b1062 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -3,7 +3,9 @@ package com.github.jengelman.gradle.plugins.shadow.internal import java.io.InputStream import org.gradle.api.Project import org.gradle.api.artifacts.Configuration +import org.gradle.api.model.ObjectFactory import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.provider.Property /** * Return `runtimeClasspath` or `runtime` configuration. @@ -13,6 +15,12 @@ internal inline val Project.runtimeConfiguration: Configuration get() { ?: configurations.getByName("runtime") } +internal inline fun ObjectFactory.property(defaultValue: T? = null): Property { + return property(T::class.java).apply { + if (defaultValue != null) convention(defaultValue) + } +} + @Suppress("NOTHING_TO_INLINE") internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy = lazy(LazyThreadSafetyMode.NONE, initializer) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index c2797ef02..48ed77f82 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -26,6 +26,7 @@ import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.DefaultCopySpec +import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask @@ -153,7 +154,15 @@ public abstract class ShadowJar : } override fun transform(clazz: Class, action: Action?): ShadowJar = apply { - val transformer = clazz.getDeclaredConstructor().newInstance() + // If the constructor takes a single ObjectFactory, inject it in. + val constructor = clazz.constructors.find { + it.parameterTypes.singleOrNull() == ObjectFactory::class.java + } + val transformer = if (constructor != null) { + objectFactory.newInstance(clazz) + } else { + clazz.getDeclaredConstructor().newInstance() + } addTransform(transformer, action) } @@ -190,7 +199,7 @@ public abstract class ShadowJar : override fun append(resourcePath: String): ShadowJar { return runCatching { transform(AppendingTransformer::class.java) { - it.resource = resourcePath + it.resource.set(resourcePath) } }.getOrDefault(this) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index e0c8cbfee..5c03b90d4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -1,14 +1,18 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.property import java.io.PrintWriter import java.nio.charset.Charset import java.text.SimpleDateFormat import java.util.Date import java.util.Locale import java.util.TreeSet +import javax.inject.Inject import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional @@ -19,52 +23,54 @@ import org.gradle.api.tasks.Optional * * @author John Engelman */ -public open class ApacheNoticeResourceTransformer : Transformer { +public open class ApacheNoticeResourceTransformer @Inject constructor( + final override val objectFactory: ObjectFactory, +) : Transformer { private val entries = mutableSetOf() private val organizationEntries = mutableMapOf>() - private val charset get() = if (encoding.isNullOrEmpty()) Charsets.UTF_8 else Charset.forName(encoding) + private inline val charset get() = Charset.forName(charsetName.get()) - /** - * MSHADE-101 :: NullPointerException when projectName is missing - */ @get:Input - public var projectName: String = "" + public open val projectName: Property = objectFactory.property("") @get:Input - public var addHeader: Boolean = true + public open val addHeader: Property = objectFactory.property(true) @get:Input - public var preamble1: String = """ - // ------------------------------------------------------------------ - // NOTICE file corresponding to the section 4d of The Apache License, - // Version 2.0, in this case for - """.trimIndent() + public open val preamble1: Property = objectFactory.property( + """ + // ------------------------------------------------------------------ + // NOTICE file corresponding to the section 4d of The Apache License, + // Version 2.0, in this case for + """.trimIndent(), + ) @get:Input - public var preamble2: String = "\n// ------------------------------------------------------------------\n" + public open val preamble2: Property = objectFactory.property( + "\n// ------------------------------------------------------------------\n", + ) @get:Input - public var preamble3: String = "This product includes software developed at\n" + public open val preamble3: Property = objectFactory.property("This product includes software developed at\n") @get:Input - public var organizationName: String = "The Apache Software Foundation" + public open val organizationName: Property = objectFactory.property("The Apache Software Foundation") @get:Input - public var organizationURL: String = "http://www.apache.org/" + public open val organizationURL: Property = objectFactory.property("http://www.apache.org/") @get:Input - public var inceptionYear: String = "2006" + public open val inceptionYear: Property = objectFactory.property("2006") @get:Optional @get:Input - public var copyright: String? = null + public open val copyright: Property = objectFactory.property() /** * The file encoding of the `NOTICE` file. */ - @get:Optional @get:Input - public var encoding: String? = null + public open val charsetName: Property = objectFactory.property(Charsets.UTF_8.name()) override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString @@ -74,6 +80,15 @@ public open class ApacheNoticeResourceTransformer : Transformer { } override fun transform(context: TransformerContext) { + val projectName = projectName.get() + val addHeader = addHeader.get() + val preamble1 = preamble1.get() + val preamble2 = preamble2.get() + val preamble3 = preamble3.get() + val organizationName = organizationName.get() + val organizationURL = organizationURL.get() + val inceptionYear = inceptionYear.get() + if (entries.isEmpty()) { val year = SimpleDateFormat("yyyy", Locale.US).format(Date()).let { if (inceptionYear != it) "$inceptionYear-$it" else it @@ -113,7 +128,7 @@ public open class ApacheNoticeResourceTransformer : Transformer { } else { val entry = sb.toString() if (entry.startsWith(projectName) && entry.contains("Copyright ")) { - copyright = entry + copyright.set(entry) } if (currentOrg == null) { entries.add(entry) @@ -140,6 +155,8 @@ public open class ApacheNoticeResourceTransformer : Transformer { override fun hasTransformedResource(): Boolean = true override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val copyright = copyright.orNull + val zipEntry = ZipEntry(NOTICE_PATH) zipEntry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, zipEntry.time) os.putNextEntry(zipEntry) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index e2926f6e8..029d2e5b8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -1,9 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.property import java.io.ByteArrayOutputStream +import javax.inject.Inject import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional @@ -16,7 +20,9 @@ import org.gradle.api.tasks.Optional */ @CacheableTransformer @Suppress("ktlint:standard:backing-property-naming") -public open class AppendingTransformer : Transformer { +public open class AppendingTransformer @Inject constructor( + final override val objectFactory: ObjectFactory, +) : Transformer { /** * Defer initialization, see [issue 763](https://github.com/GradleUp/shadow/issues/763). */ @@ -25,10 +31,10 @@ public open class AppendingTransformer : Transformer { @get:Optional @get:Input - public var resource: String? = null + public open val resource: Property = objectFactory.property() override fun canTransformResource(element: FileTreeElement): Boolean { - return resource.equals(element.relativePath.pathString, ignoreCase = true) + return resource.orNull.equals(element.relativePath.pathString, ignoreCase = true) } override fun transform(context: TransformerContext) { @@ -43,7 +49,7 @@ public open class AppendingTransformer : Transformer { } override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(requireNotNull(resource)) + val entry = ZipEntry(resource.get()) entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index e39a30a04..f28ad6e7e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -1,6 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.property +import javax.inject.Inject import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional @@ -11,13 +15,15 @@ import org.gradle.api.tasks.Optional * * @author John Engelman */ -public open class DontIncludeResourceTransformer : Transformer by NoOpTransformer { +public open class DontIncludeResourceTransformer @Inject constructor( + final override val objectFactory: ObjectFactory, +) : Transformer by NoOpTransformer { @get:Optional @get:Input - public var resource: String? = null + public open val resource: Property = objectFactory.property() override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString - return !resource.isNullOrEmpty() && path.endsWith(resource!!) + return !resource.orNull.isNullOrEmpty() && path.endsWith(resource.get()) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index 14f20385c..5ca9bb1ec 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -1,9 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp -import java.io.File +import javax.inject.Inject import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.PathSensitive @@ -16,22 +20,24 @@ import org.gradle.api.tasks.PathSensitivity * * @author John Engelman */ -public open class IncludeResourceTransformer : Transformer by NoOpTransformer { +public open class IncludeResourceTransformer @Inject constructor( + final override val objectFactory: ObjectFactory, +) : Transformer by NoOpTransformer { @get:InputFile @get:PathSensitive(PathSensitivity.NONE) - public var file: File? = null + public open val file: RegularFileProperty = objectFactory.fileProperty() @get:Input - public var resource: String? = null + public open val resource: Property = objectFactory.property() - override fun hasTransformedResource(): Boolean = file?.exists() == true + override fun hasTransformedResource(): Boolean = file.get().asFile.exists() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(requireNotNull(resource)) + val entry = ZipEntry(resource.get()) entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) - requireNotNull(file).inputStream().use { inputStream -> + file.get().asFile.inputStream().use { inputStream -> inputStream.copyTo(os) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 80f306343..473c81568 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -3,9 +3,12 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import java.io.ByteArrayOutputStream import java.io.IOException import java.util.jar.JarFile.MANIFEST_NAME +import javax.inject.Inject import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty import org.gradle.api.tasks.Input import org.slf4j.LoggerFactory @@ -17,12 +20,15 @@ import org.slf4j.LoggerFactory * Modified from [ManifestResourceTransformer]. * @author Chris Rankin */ -public open class ManifestAppenderTransformer : Transformer { +public open class ManifestAppenderTransformer @Inject constructor( + final override val objectFactory: ObjectFactory, +) : Transformer { private var manifestContents = ByteArray(0) - private val _attributes = mutableListOf>>() + @Suppress("UNCHECKED_CAST") @get:Input - public open val attributes: List>> get() = _attributes + public open val attributes: ListProperty>> = + objectFactory.listProperty(Pair::class.java) as ListProperty>> override fun canTransformResource(element: FileTreeElement): Boolean { return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) @@ -42,7 +48,7 @@ public open class ManifestAppenderTransformer : Transformer { } } - override fun hasTransformedResource(): Boolean = _attributes.isNotEmpty() + override fun hasTransformedResource(): Boolean = attributes.get().isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { val entry = ZipEntry(MANIFEST_NAME) @@ -50,20 +56,20 @@ public open class ManifestAppenderTransformer : Transformer { os.putNextEntry(entry) os.write(manifestContents) - if (_attributes.isNotEmpty()) { - for ((key, value) in _attributes) { + if (attributes.get().isNotEmpty()) { + for ((key, value) in attributes.get()) { os.write(key.toByteArray()) os.write(SEPARATOR) os.write(value.toString().toByteArray()) os.write(EOL) } os.write(EOL) - _attributes.clear() + attributes.empty() } } public open fun append(name: String, value: Comparable<*>): ManifestAppenderTransformer = apply { - _attributes.add(Pair(name, value)) + attributes.add(Pair(name, value)) } private companion object { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index bc03988fc..c63fde158 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -1,13 +1,18 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp import java.io.IOException import java.util.jar.Attributes import java.util.jar.JarFile import java.util.jar.Manifest +import javax.inject.Inject import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional import org.slf4j.LoggerFactory @@ -22,17 +27,20 @@ import org.slf4j.LoggerFactory * @author Jason van Zyl * @author John Engelman */ -public open class ManifestResourceTransformer : Transformer { +public open class ManifestResourceTransformer @Inject constructor( + final override val objectFactory: ObjectFactory, +) : Transformer { private var manifestDiscovered = false private var manifest: Manifest? = null @get:Optional @get:Input - public var mainClass: String? = null + public open val mainClass: Property = objectFactory.property() @get:Optional @get:Input - public var manifestEntries: MutableMap? = null + public open val manifestEntries: MapProperty = + objectFactory.mapProperty(String::class.java, Attributes::class.java) override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString @@ -62,10 +70,10 @@ public open class ManifestResourceTransformer : Transformer { } val attributes = manifest!!.mainAttributes - mainClass?.let { + mainClass.orNull?.let { attributes[Attributes.Name.MAIN_CLASS] = it } - manifestEntries?.forEach { (key, value) -> + manifestEntries.get().forEach { (key, value) -> attributes[Attributes.Name(key)] = value } @@ -76,10 +84,7 @@ public open class ManifestResourceTransformer : Transformer { } public open fun attributes(attributes: Map): ManifestResourceTransformer = apply { - if (manifestEntries == null) { - manifestEntries = LinkedHashMap() - } - manifestEntries!!.putAll(attributes) + manifestEntries.putAll(attributes) } private companion object { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 79208cfd5..b09a42921 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -1,15 +1,23 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties +import com.github.jengelman.gradle.plugins.shadow.internal.property +import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy +import groovy.lang.Closure +import groovy.lang.Closure.IDENTITY import java.io.ByteArrayOutputStream import java.io.InputStream import java.io.InputStreamReader import java.nio.charset.Charset import java.util.Properties -import java.util.function.Function +import javax.inject.Inject import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal @@ -17,12 +25,12 @@ import org.gradle.api.tasks.Internal * Resources transformer that merges Properties files. * * The default merge strategy discards duplicate values coming from additional - * resources. This behavior can be changed by setting a value for the `mergeStrategy` - * property, such as 'first' (default), 'latest' or 'append'. If the merge strategy is - * 'latest' then the last value of a matching property entry will be used. If the - * merge strategy is 'append' then the property values will be combined, using a + * resources. This behavior can be changed by setting a value for the [mergeStrategy] property, + * such as [MergeStrategy.First] (default), [MergeStrategy.Latest] or [MergeStrategy.Append]. If the merge strategy is + * [MergeStrategy.Latest] then the last value of a matching property entry will be used. If the + * merge strategy is [MergeStrategy.Append] then the property values will be combined, using a * merge separator (default value is ','). The merge separator can be changed by - * setting a value for the `mergeSeparator` property. + * setting a value for the [mergeSeparator] property. * * Say there are two properties files A and B with the * following entries: @@ -35,40 +43,40 @@ import org.gradle.api.tasks.Internal * - key2 = balue2 * - key3 = value3 * - * With `mergeStrategy = first` you get + * With `mergeStrategy = MergeStrategy.First` you get * * **C** * - key1 = value1 * - key2 = value2 * - key3 = value3 * - * With `mergeStrategy = latest` you get + * With `mergeStrategy = MergeStrategy.Latest` you get * * **C** * - key1 = value1 * - key2 = balue2 * - key3 = value3 * - * With `mergeStrategy = append` and `mergeSeparator = ;` you get + * With `mergeStrategy = MergeStrategy.Append` and `mergeSeparator = ;` you get * * **C** * - key1 = value1 * - key2 = value2;balue2 * - key3 = value3 * - * There are three additional properties that can be set: `paths`, `mappings`, - * and `keyTransformer`. + * There are three additional properties that can be set: [paths], [mappings], + * and [keyTransformer]. * The first contains a list of strings or regexes that will be used to determine if * a path should be transformed or not. The merge strategy and merge separator are * taken from the global settings. * - * The `mappings` property allows you to define merge strategy and separator per - * path. If either `paths` or `mappings` is defined then no other path - * entries will be merged. `mappings` has precedence over `paths` if both + * The [mappings] property allows you to define merge strategy and separator per + * path. If either [paths] or [mappings] is defined then no other path + * entries will be merged. [mappings] has precedence over [paths] if both * are defined. * * If you need to transform keys in properties files, e.g. because they contain class - * names about to be relocated, you can set the `keyTransformer` property to a + * names about to be relocated, you can set the [keyTransformer] property to a * closure that receives the original key and returns the key name to be used. * * Example: @@ -91,35 +99,38 @@ import org.gradle.api.tasks.Internal * @author Andres Almiray * @author Marc Philipp */ -public open class PropertiesFileTransformer : Transformer { +public open class PropertiesFileTransformer @Inject constructor( + final override val objectFactory: ObjectFactory, +) : Transformer { private val propertiesEntries = mutableMapOf() - private val _charset get() = Charset.forName(charset) + private inline val charset get() = Charset.forName(charsetName.get()) @get:Input - public var paths: List = listOf() + public open val paths: ListProperty = objectFactory.listProperty(String::class.java) + @Suppress("UNCHECKED_CAST") @get:Input - public var mappings: Map> = mapOf() + public open val mappings: MapProperty> = + objectFactory.mapProperty(String::class.java, Map::class.java) as MapProperty> - /** - * Optional values: first, latest, append. - */ @get:Input - public var mergeStrategy: String = "first" + public open val mergeStrategy: Property = objectFactory.property(MergeStrategy.First) @get:Input - public var mergeSeparator: String = "," + public open val mergeSeparator: Property = objectFactory.property(",") @get:Input - public var charset: String = "ISO_8859_1" + public open val charsetName: Property = objectFactory.property(Charsets.ISO_8859_1.name()) - /** - * Use [java.util.function.Function] here for compatibility with Groovy and Java. - */ + @Suppress("UNCHECKED_CAST") @get:Internal - public var keyTransformer: Function = IDENTITY + public open val keyTransformer: Property> = + objectFactory.property(IDENTITY) as Property> override fun canTransformResource(element: FileTreeElement): Boolean { + val mappings = mappings.get() + val paths = paths.get() + val path = element.relativePath.pathString if (path in mappings) return true for (key in mappings.keys) { @@ -140,11 +151,14 @@ public open class PropertiesFileTransformer : Transformer { } else { for ((key, value) in incoming) { if (props.containsKey(key)) { - when (mergeStrategyFor(context.path).lowercase()) { - "latest" -> props[key] = value - "append" -> props[key] = props.getProperty(key as String) + mergeSeparatorFor(context.path) + value - "first" -> Unit - else -> Unit + when (MergeStrategy.from(mergeStrategyFor(context.path))) { + MergeStrategy.Latest -> { + props[key] = value + } + MergeStrategy.Append -> { + props[key] = props.getProperty(key as String) + mergeSeparatorFor(context.path) + value + } + MergeStrategy.First -> Unit } } else { props[key] = value @@ -156,22 +170,25 @@ public open class PropertiesFileTransformer : Transformer { private fun loadAndTransformKeys(inputStream: InputStream): CleanProperties { val props = CleanProperties() // InputStream closed by caller, so we don't do it here. - props.load(inputStream.reader(_charset)) + props.load(inputStream.reader(charset)) return transformKeys(props) } private fun transformKeys(properties: Properties): CleanProperties { - if (keyTransformer === IDENTITY) { + if (keyTransformer == IDENTITY) { return properties as CleanProperties } val result = CleanProperties() properties.forEach { (key, value) -> - result[keyTransformer.apply(key as String)] = value + result[keyTransformer.get().call(key as String)] = value } return result } private fun mergeStrategyFor(path: String): String { + val mappings = mappings.get() + val mergeStrategy = mergeStrategy.get().name + mappings[path]?.let { return it["mergeStrategy"] ?: mergeStrategy } @@ -184,6 +201,9 @@ public open class PropertiesFileTransformer : Transformer { } private fun mergeSeparatorFor(path: String): String { + val mappings = mappings.get() + val mergeSeparator = mergeSeparator.get() + mappings[path]?.let { return it["mergeSeparator"] ?: mergeSeparator } @@ -201,7 +221,7 @@ public open class PropertiesFileTransformer : Transformer { override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { // cannot close the writer as the OutputStream needs to remain open - val zipWriter = os.writer(_charset) + val zipWriter = os.writer(charset) propertiesEntries.forEach { (path, props) -> val entry = ZipEntry(path) entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) @@ -216,14 +236,29 @@ public open class PropertiesFileTransformer : Transformer { private fun Properties.toReader(): InputStreamReader { val os = ByteArrayOutputStream() - os.writer(Charset.forName(charset)).use { writer -> + os.writer(charset).use { writer -> store(writer, "") } - return os.toByteArray().inputStream().reader(_charset) + return os.toByteArray().inputStream().reader(charset) + } + + public enum class MergeStrategy { + First, + Latest, + Append, + ; + + public companion object { + @JvmStatic + public fun from(value: String): MergeStrategy { + @OptIn(ExperimentalStdlibApi::class) + return entries.find { it.name.equals(value, ignoreCase = true) } + ?: throw IllegalArgumentException("Unknown merge strategy: $value") + } + } } private companion object { private const val PROPERTIES_SUFFIX = ".properties" - private val IDENTITY = Function { it } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index 0f561829b..4fea17f84 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Named import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.Internal /** @@ -24,6 +25,15 @@ public interface Transformer : Named { @Internal override fun getName(): String = this::class.java.simpleName + + /** + * This is used for creating Gradle's lazy properties in the subclass, Shadow's build-in transformers that depend on + * this have been injected via [ObjectFactory.newInstance]. Custom transformers should implement or inject + * this property if they need to access it. + */ + @get:Internal + public val objectFactory: ObjectFactory + get() = throw NotImplementedError("You have to make sure this has been implemented or injected.") } public object NoOpTransformer : Transformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index 9694517d3..da28716b6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -1,9 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.property import java.io.StringReader +import javax.inject.Inject import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional import org.jdom2.Document @@ -23,25 +27,27 @@ import org.xml.sax.InputSource * @author John Engelman */ @CacheableTransformer -public open class XmlAppendingTransformer : Transformer { +public open class XmlAppendingTransformer @Inject constructor( + final override val objectFactory: ObjectFactory, +) : Transformer { private var doc: Document? = null @get:Input - public var ignoreDtd: Boolean = true + public open val ignoreDtd: Property = objectFactory.property(true) @get:Optional @get:Input - public var resource: String? = null + public open val resource: Property = objectFactory.property() override fun canTransformResource(element: FileTreeElement): Boolean { - return resource?.equals(element.relativePath.pathString, ignoreCase = true) == true + return resource.orNull?.equals(element.relativePath.pathString, ignoreCase = true) == true } override fun transform(context: TransformerContext) { val r = try { SAXBuilder(XMLReaders.NONVALIDATING).apply { expandEntities = false - if (ignoreDtd) { + if (ignoreDtd.get()) { entityResolver = EntityResolver { _, _ -> InputSource(StringReader("")) } } }.build(context.inputStream) @@ -69,7 +75,7 @@ public open class XmlAppendingTransformer : Transformer { override fun hasTransformedResource(): Boolean = doc != null override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(resource) + val entry = ZipEntry(resource.get()) entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) XMLOutputter(Format.getPrettyFormat()).output(doc, os) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy index f9eb12b58..aac98bc18 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy @@ -758,7 +758,7 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( 'ComponentsXmlResourceTransformer' | '' 'DontIncludeResourceTransformer' | '' 'GroovyExtensionModuleTransformer' | '' - 'IncludeResourceTransformer' | '{ resource = "test.file"; file = file("test/some.file") }' + 'IncludeResourceTransformer' | '{ resource.set("test.file"); file.fileValue(file("test/some.file")) }' 'Log4j2PluginsCacheFileTransformer' | '' 'ManifestAppenderTransformer' | '' 'ManifestResourceTransformer' | '' diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy index e81f6ab8b..d5e0932d0 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy @@ -39,7 +39,7 @@ class ApacheNoticeResourceTransformerParameterTests extends TransformerTestSuppo @BeforeEach void setUp() { - transformer = new ApacheNoticeResourceTransformer() + transformer = new ApacheNoticeResourceTransformer(objectFactory) stats = new ShadowStats() } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy index 0b2646aa8..4cb7ff0f7 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy @@ -45,7 +45,7 @@ class ApacheNoticeResourceTransformerTest extends TransformerTestSupport { protected static T transformer + protected static final def objectFactory = ProjectBuilder.builder().build().objects protected static FileTreeElement getFileElement(String path) { return new DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy index a33cb56d8..51cfb6c62 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy @@ -45,12 +45,12 @@ class XmlAppendingTransformerTest extends TransformerTestSupport Date: Wed, 27 Nov 2024 13:04:34 +0800 Subject: [PATCH 034/941] Replace project's objectFactory with task's --- .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 48ed77f82..f0a1ab085 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -66,7 +66,7 @@ public abstract class ShadowJar : @get:Classpath public val toMinimize: ConfigurableFileCollection by unsafeLazy { - project.objects.fileCollection().apply { + objectFactory.fileCollection().apply { if (minimizeJar.get()) { setFrom(dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) } @@ -75,7 +75,7 @@ public abstract class ShadowJar : @get:Classpath public val apiJars: ConfigurableFileCollection by unsafeLazy { - project.objects.fileCollection().apply { + objectFactory.fileCollection().apply { if (minimizeJar.get()) { setFrom(UnusedTracker.getApiJarsFromProject(project)) } @@ -85,7 +85,7 @@ public abstract class ShadowJar : @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) public val sourceSetsClassesDirs: ConfigurableFileCollection by unsafeLazy { - project.objects.fileCollection().apply { + objectFactory.fileCollection().apply { if (minimizeJar.get()) { project.extensions.getByType(SourceSetContainer::class.java).forEach { sourceSet -> from(sourceSet.output.classesDirs.filter { it.isDirectory }) From 59676b7bd05716bb48dff73276e34ca3e6a6726e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 27 Nov 2024 07:58:50 -0500 Subject: [PATCH 035/941] Migrate SimpleRelocator to using lazy properties (#1047) * Migrate SimpleRelocator * Rename isRawString to rawString * Fix * Add SimpleRelocatorParent * Don't need to assign pattern and shadedPattern for raw string relocator * Revert "Add SimpleRelocatorParent" This reverts commit 11f67a3abfc4af476cafb4119946f7c8f9f87666. * Set rawString explicitly * Note this change * Suppress LeakingThis * Tweak kdoc * Revert "Set rawString explicitly" This reverts commit 1c5e644695f8c384ab7ea75d1144336d49e0e17d. --- api/shadow.api | 24 ++-- src/docs/changes/README.md | 1 + .../shadow/relocation/SimpleRelocator.kt | 113 +++++++++--------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 4 +- .../SimpleRelocatorParameterTest.groovy | 4 +- .../relocation/SimpleRelocatorTest.groovy | 36 +++--- ...g4j2PluginsCacheFileTransformerSpec.groovy | 6 +- 7 files changed, 97 insertions(+), 91 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 8b962c1e4..2992b7941 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -181,23 +181,23 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat } public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator : com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { - public fun (Ljava/lang/String;Ljava/lang/String;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V + public synthetic fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; public fun canRelocateClass (Ljava/lang/String;)Z public fun canRelocatePath (Ljava/lang/String;)Z public fun exclude (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; - public fun getExcludes ()Ljava/util/Set; - public fun getIncludes ()Ljava/util/Set; - public fun getPathPattern ()Ljava/lang/String; - public fun getPattern ()Ljava/lang/String; - public fun getShadedPathPattern ()Ljava/lang/String; - public fun getShadedPattern ()Ljava/lang/String; + public fun getExcludes ()Lorg/gradle/api/provider/SetProperty; + public fun getIncludes ()Lorg/gradle/api/provider/SetProperty; + public fun getPathPattern ()Lorg/gradle/api/provider/Property; + public fun getPattern ()Lorg/gradle/api/provider/Property; + public fun getRawString ()Lorg/gradle/api/provider/Property; + public fun getShadedPathPattern ()Lorg/gradle/api/provider/Property; + public fun getShadedPattern ()Lorg/gradle/api/provider/Property; public fun include (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; - public fun isRawString ()Z public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; } diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 6b159a93f..db71f9450 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -12,6 +12,7 @@ - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) - **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) - **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) +- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) `isEnableRelocation` is deprecated, use `enableRelocation` instead. - **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 86c34da73..12dd9cf4c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -1,93 +1,92 @@ package com.github.jengelman.gradle.plugins.shadow.relocation +import com.github.jengelman.gradle.plugins.shadow.internal.property import java.util.regex.Pattern import org.codehaus.plexus.util.SelectorUtils +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional /** - * Modified from `org.apache.maven.plugins.shade.relocation.SimpleRelocator.java` + * Modified from [org.apache.maven.plugins.shade.relocation.SimpleRelocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java). * * @author Jason van Zyl * @author Mauro Talevi * @author John Engelman */ +@Suppress("LeakingThis") @CacheableRelocator public open class SimpleRelocator @JvmOverloads constructor( + objectFactory: ObjectFactory, pattern: String?, shadedPattern: String?, includes: List? = null, excludes: List? = null, - private val _isRawString: Boolean = false, + rawString: Boolean = false, ) : Relocator { - private val _pattern: String? - private val _pathPattern: String - private val _shadedPattern: String? - private val _shadedPathPattern: String - private val _includes = mutableSetOf() - private val _excludes = mutableSetOf() - - init { - if (_isRawString) { - _pathPattern = pattern.orEmpty() - _shadedPathPattern = shadedPattern.orEmpty() - _pattern = null // not used for raw string relocator - _shadedPattern = null // not used for raw string relocator - } else { - if (pattern == null) { - _pattern = "" - _pathPattern = "" - } else { - _pattern = pattern.replace('/', '.') - _pathPattern = pattern.replace('.', '/') - } - if (shadedPattern == null) { - _shadedPattern = "hidden.${_pattern}" - _shadedPathPattern = "hidden/$_pathPattern" - } else { - _shadedPattern = shadedPattern.replace('/', '.') - _shadedPathPattern = shadedPattern.replace('.', '/') - } - } - _includes += normalizePatterns(includes) - _excludes += normalizePatterns(excludes) - } @get:Input @get:Optional - public open val pattern: String? get() = _pattern + public open val pattern: Property = objectFactory.property() @get:Input - public open val pathPattern: String get() = _pathPattern + public open val pathPattern: Property = objectFactory.property() @get:Input @get:Optional - public open val shadedPattern: String? get() = _shadedPattern + public open val shadedPattern: Property = objectFactory.property() @get:Input - public open val shadedPathPattern: String get() = _shadedPathPattern + public open val shadedPathPattern: Property = objectFactory.property() @get:Input - public open val isRawString: Boolean get() = _isRawString + public open val rawString: Property = objectFactory.property(rawString) @get:Input - public open val includes: Set get() = _includes + public open val includes: SetProperty = objectFactory.setProperty(String::class.java) @get:Input - public open val excludes: Set get() = _excludes + public open val excludes: SetProperty = objectFactory.setProperty(String::class.java) + + init { + if (rawString) { + pathPattern.set(pattern.orEmpty()) + shadedPathPattern.set(shadedPattern.orEmpty()) + // Don't need to assign pattern and shadedPattern for raw string relocator + } else { + if (pattern == null) { + this.pattern.set("") + this.pathPattern.set("") + } else { + this.pattern.set(pattern.replace('/', '.')) + this.pathPattern.set(pattern.replace('.', '/')) + } + if (shadedPattern == null) { + this.shadedPattern.set(this.pattern.map { "hidden.$it" }) + this.shadedPathPattern.set(this.pathPattern.map { "hidden/$it" }) + } else { + this.shadedPattern.set(shadedPattern.replace('/', '.')) + this.shadedPathPattern.set(shadedPattern.replace('.', '/')) + } + } + this.includes.addAll(normalizePatterns(includes)) + this.excludes.addAll(normalizePatterns(excludes)) + } public open fun include(pattern: String): SimpleRelocator = apply { - _includes += normalizePatterns(listOf(pattern)) + includes.addAll(normalizePatterns(listOf(pattern))) } public open fun exclude(pattern: String): SimpleRelocator = apply { - _excludes += normalizePatterns(listOf(pattern)) + excludes.addAll(normalizePatterns(listOf(pattern))) } override fun canRelocatePath(path: String): Boolean { - if (_isRawString) return Pattern.compile(_pathPattern).matcher(path).find() + if (rawString.get()) return Pattern.compile(pathPattern.get()).matcher(path).find() // If string is too short - no need to perform expensive string operations - if (path.length < _pathPattern.length) return false + if (path.length < pathPattern.get().length) return false val adjustedPath = if (path.endsWith(".class")) { // Safeguard against strings containing only ".class" if (path.length == 6) return false @@ -97,34 +96,34 @@ public open class SimpleRelocator @JvmOverloads constructor( } // Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties"). val startIndex = if (adjustedPath.startsWith("/")) 1 else 0 - val pathStartsWithPattern = adjustedPath.startsWith(_pathPattern, startIndex) + val pathStartsWithPattern = adjustedPath.startsWith(pathPattern.get(), startIndex) return pathStartsWithPattern && isIncluded(adjustedPath) && !isExcluded(adjustedPath) } override fun canRelocateClass(className: String): Boolean { - return !_isRawString && !className.contains('/') && canRelocatePath(className.replace('.', '/')) + return !rawString.get() && !className.contains('/') && canRelocatePath(className.replace('.', '/')) } override fun relocatePath(context: RelocatePathContext): String { val path = context.path - context.stats.relocate(_pathPattern, _shadedPathPattern) - return if (_isRawString) { - path.replace(_pathPattern.toRegex(), _shadedPathPattern) + context.stats.relocate(pathPattern.get(), shadedPathPattern.get()) + return if (rawString.get()) { + path.replace(pathPattern.get().toRegex(), shadedPathPattern.get()) } else { - path.replaceFirst(_pathPattern, _shadedPathPattern) + path.replaceFirst(pathPattern.get(), shadedPathPattern.get()) } } override fun relocateClass(context: RelocateClassContext): String { - context.stats.relocate(_pathPattern, _shadedPathPattern) - return context.className.replaceFirst(_pattern.orEmpty(), _shadedPattern.orEmpty()) + context.stats.relocate(pathPattern.get(), shadedPathPattern.get()) + return context.className.replaceFirst(pattern.orNull.orEmpty(), shadedPattern.orNull.orEmpty()) } override fun applyToSourceContent(sourceContent: String): String { - return if (_isRawString) { + return if (rawString.get()) { sourceContent } else { - sourceContent.replace("\\b$_pattern".toRegex(), _shadedPattern.orEmpty()) + sourceContent.replace("\\b${pattern.orNull}".toRegex(), shadedPattern.orNull.orEmpty()) } } @@ -147,10 +146,10 @@ public open class SimpleRelocator @JvmOverloads constructor( } private fun isIncluded(path: String): Boolean { - return _includes.isEmpty() || _includes.any { SelectorUtils.matchPath(it, path, "/", true) } + return includes.get().isEmpty() || includes.get().any { SelectorUtils.matchPath(it, path, "/", true) } } private fun isExcluded(path: String): Boolean { - return _excludes.any { SelectorUtils.matchPath(it, path, "/", true) } + return excludes.get().any { SelectorUtils.matchPath(it, path, "/", true) } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index f0a1ab085..ee9c18ec7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -213,7 +213,7 @@ public abstract class ShadowJar : destination: String, action: Action?, ): ShadowJar = apply { - val relocator = SimpleRelocator(pattern, destination) + val relocator = SimpleRelocator(objectFactory, pattern, destination) addRelocator(relocator, action) } @@ -289,7 +289,7 @@ public abstract class ShadowJar : jarFile.entries().toList() .filter { it.name.endsWith(".class") && it.name != "module-info.class" } .map { it.name.substringBeforeLast('/').replace('/', '.') } - .map { SimpleRelocator(it, "$prefix.$it") } + .map { SimpleRelocator(objectFactory, it, "$prefix.$it") } } } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy index 9e47404ca..3cab488f9 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy @@ -19,6 +19,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation +import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Test import static org.junit.jupiter.api.Assertions.fail @@ -31,6 +32,7 @@ import static org.junit.jupiter.api.Assertions.fail * @author John Engelman */ class SimpleRelocatorParameterTest { + private static final def objectFactory = ProjectBuilder.builder().build().objects @Test void testThatNullPatternInConstructorShouldNotThrowNullPointerException() { @@ -44,7 +46,7 @@ class SimpleRelocatorParameterTest { private static void constructThenFailOnNullPointerException(String pattern, String shadedPattern) { try { - new SimpleRelocator(pattern, shadedPattern) + new SimpleRelocator(objectFactory, pattern, shadedPattern) } catch (NullPointerException ignored) { fail("Constructor should not throw null pointer exceptions") diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy index f1488c523..5d0eb0b98 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy @@ -20,6 +20,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -38,6 +39,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals */ class SimpleRelocatorTest { + private static final def objectFactory = ProjectBuilder.builder().build().objects private static ShadowStats stats @BeforeEach @@ -49,7 +51,7 @@ class SimpleRelocatorTest { void testCanRelocatePath() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator(objectFactory, "org.foo", null) assertEquals(true, relocator.canRelocatePath("org/foo/Class")) assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class")) @@ -63,7 +65,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("/org/Foo/Class")) assertEquals(false, relocator.canRelocatePath("/org/Foo/Class.class")) - relocator = new SimpleRelocator("org.foo", null, null, + relocator = new SimpleRelocator(objectFactory, "org.foo", null, null, List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) assertEquals(true, relocator.canRelocatePath("org/foo/Class")) assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) @@ -90,7 +92,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class.class")) // Verify edge cases - relocator = new SimpleRelocator("org.f", null) + relocator = new SimpleRelocator(objectFactory, "org.f", null) assertEquals(false, relocator.canRelocatePath("")) // Empty path assertEquals(false, relocator.canRelocatePath(".class")) // only .class assertEquals(false, relocator.canRelocatePath("te")) // shorter than path pattern @@ -104,7 +106,7 @@ class SimpleRelocatorTest { SimpleRelocator relocator // Include with Regex - relocator = new SimpleRelocator("org.foo", null, Collections.singletonList("%regex[org/foo/R(\\\$.*)?\$]"), null) + relocator = new SimpleRelocator(objectFactory, "org.foo", null, Collections.singletonList("%regex[org/foo/R(\\\$.*)?\$]"), null) assertEquals(true, relocator.canRelocatePath("org/foo/R.class")) assertEquals(true, relocator.canRelocatePath("org/foo/R\$string.class")) assertEquals(true, relocator.canRelocatePath("org/foo/R\$layout.class")) @@ -115,7 +117,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/R\$string.class")) // Exclude with Regex - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator(objectFactory, "org.foo", null) relocator.exclude("%regex[org/foo/.*Factory[0-9].*]") assertEquals(true, relocator.canRelocatePath("org/foo/Factory.class")) assertEquals(true, relocator.canRelocatePath("org/foo/FooFactoryMain.class")) @@ -125,7 +127,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/foo/BarFactory2.class")) // Include with Regex and normal pattern - relocator = new SimpleRelocator("org.foo", null, + relocator = new SimpleRelocator(objectFactory, "org.foo", null, List.of("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*")) assertEquals(true, relocator.canRelocatePath("org/foo/Factory1.class")) assertEquals(true, relocator.canRelocatePath("org/foo/public/Bar.class")) @@ -137,13 +139,13 @@ class SimpleRelocatorTest { void testCanRelocateClass() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator(objectFactory, "org.foo", null) assertEquals(true, relocator.canRelocateClass("org.foo.Class")) assertEquals(true, relocator.canRelocateClass("org.foo.bar.Class")) assertEquals(false, relocator.canRelocateClass("com.foo.bar.Class")) assertEquals(false, relocator.canRelocateClass("org.Foo.Class")) - relocator = new SimpleRelocator("org.foo", null, null, + relocator = new SimpleRelocator(objectFactory, "org.foo", null, null, List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) assertEquals(true, relocator.canRelocateClass("org.foo.Class")) assertEquals(true, relocator.canRelocateClass("org.foo.excluded")) @@ -166,17 +168,17 @@ class SimpleRelocatorTest { void testCanRelocateRawString() { SimpleRelocator relocator - relocator = new SimpleRelocator("org/foo", null, null, null, true) + relocator = new SimpleRelocator(objectFactory, "org/foo", null, null, null, true) assertEquals(true, relocator.canRelocatePath("(I)org/foo/bar/Class")) - relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", null, null, null, true) + relocator = new SimpleRelocator(objectFactory, "^META-INF/org.foo.xml\$", null, null, null, true) assertEquals(true, relocator.canRelocatePath("META-INF/org.foo.xml")) } //MSHADE-119, make sure that the easy part of this works. @Test void testCanRelocateAbsClassPath() { - SimpleRelocator relocator = new SimpleRelocator("org.apache.velocity", "org.apache.momentum") + SimpleRelocator relocator = new SimpleRelocator(objectFactory, "org.apache.velocity", "org.apache.momentum") assertEquals("/org/apache/momentum/mass.properties", relocator.relocatePath(pathContext("/org/apache/velocity/mass.properties"))) } @@ -185,10 +187,10 @@ class SimpleRelocatorTest { void testRelocatePath() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator(objectFactory, "org.foo", null) assertEquals("hidden/org/foo/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) - relocator = new SimpleRelocator("org.foo", "private.stuff") + relocator = new SimpleRelocator(objectFactory, "org.foo", "private.stuff") assertEquals("private/stuff/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) } @@ -196,10 +198,10 @@ class SimpleRelocatorTest { void testRelocateClass() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator(objectFactory, "org.foo", null) assertEquals("hidden.org.foo.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) - relocator = new SimpleRelocator("org.foo", "private.stuff") + relocator = new SimpleRelocator(objectFactory, "org.foo", "private.stuff") assertEquals("private.stuff.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) } @@ -207,10 +209,10 @@ class SimpleRelocatorTest { void testRelocateRawString() { SimpleRelocator relocator - relocator = new SimpleRelocator("Lorg/foo", "Lhidden/org/foo", null, null, true) + relocator = new SimpleRelocator(objectFactory, "Lorg/foo", "Lhidden/org/foo", null, null, true) assertEquals("(I)Lhidden/org/foo/bar/Class", relocator.relocatePath(pathContext("(I)Lorg/foo/bar/Class"))) - relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", null, null, true) + relocator = new SimpleRelocator(objectFactory, "^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", null, null, true) assertEquals("META-INF/hidden.org.foo.xml", relocator.relocatePath(pathContext("META-INF/org.foo.xml"))) } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy index e48aaf66a..b718f52b5 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy @@ -5,6 +5,7 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import org.apache.logging.log4j.core.config.plugins.processor.PluginCache import org.apache.tools.zip.ZipOutputStream +import org.gradle.testfixtures.ProjectBuilder import spock.lang.Specification import static java.util.Collections.singletonList @@ -17,6 +18,7 @@ import static org.apache.logging.log4j.core.config.plugins.processor.PluginProce * @see LinkedIn */ class Log4j2PluginsCacheFileTransformerSpec extends Specification { + private static final def objectFactory = ProjectBuilder.builder().build().objects Log4j2PluginsCacheFileTransformer transformer @@ -35,7 +37,7 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { void "should transform"() { given: List relocators = new ArrayList<>() - relocators.add(new SimpleRelocator(null, null)) + relocators.add(new SimpleRelocator(objectFactory, null, null)) when: transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators)) @@ -49,7 +51,7 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { String pattern = "org.apache.logging" String destination = "new.location.org.apache.logging" - List relocators = singletonList((Relocator) new SimpleRelocator(pattern, destination)) + List relocators = singletonList((Relocator) new SimpleRelocator(objectFactory, pattern, destination)) when: transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators, new ShadowStats())) From 617231bb796dc27db95d0b929172eebb96960a52 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 27 Nov 2024 21:07:40 +0800 Subject: [PATCH 036/941] Prepare version 9.0.0-beta1 --- gradle.properties | 2 +- src/docs/changes/README.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c64924434..ec3cb908f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.configuration-cache.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta1 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index db71f9450..417681c54 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased] + +## [v9.0.0-beta1] (2024-11-27) + **Added** - Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) From 8ebd2d3ba0909ab7117a6ac17113fa316fcc1ee9 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 27 Nov 2024 21:09:22 +0800 Subject: [PATCH 037/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ec3cb908f..c64924434 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.configuration-cache.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta1 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 4c1c975c4cad9ddc710c88f6a4a2862c8e88d501 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 27 Nov 2024 21:10:29 +0800 Subject: [PATCH 038/941] Update changelog --- src/docs/changes/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 417681c54..297931619 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -14,10 +14,10 @@ - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) - **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) -- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) -- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) +- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) `isEnableRelocation` is deprecated, use `enableRelocation` instead. - **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) +- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) **Removed** @@ -25,7 +25,7 @@ **Fixed** -- Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)). +- Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) ## [v8.3.5] (2024-11-03) From 21e85a6dcef696a322593aa5aec778abd80a03af Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 28 Nov 2024 09:43:21 +0800 Subject: [PATCH 039/941] chore(deps): update plugin org.jetbrains.kotlin.jvm to v2.1.0 (#1051) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 786c6f381..1c02867c0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,7 +2,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion plugins { - kotlin("jvm") version "2.0.21" + kotlin("jvm") version "2.1.0" groovy // Required for Spock tests. id("shadow.convention.publish") id("shadow.convention.deploy") From efb6ba266dac540701d857b399201cbfd725c7cc Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 28 Nov 2024 10:17:53 +0800 Subject: [PATCH 040/941] Update changelog --- src/docs/changes/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 297931619..bce32a706 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -439,7 +439,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Wed, 27 Nov 2024 23:11:50 -0500 Subject: [PATCH 041/941] Revert changes in SimpleRelocator (#1052) * Revert "Migrate SimpleRelocator to using lazy properties (#1047)" This reverts commit 59676b7b * Still rename isRawString to rawString * Optimize normalizePatterns * Mark pattern and shadedPattern non-null * Inline getters * Note this change * Revert "Inline getters" This reverts commit b05bc7d29b6f01fa72285f9c94a904ea0249aafe. --- api/shadow.api | 24 +-- src/docs/changes/README.md | 5 + .../shadow/relocation/SimpleRelocator.kt | 150 +++++++++--------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 4 +- .../SimpleRelocatorParameterTest.groovy | 4 +- .../relocation/SimpleRelocatorTest.groovy | 36 ++--- ...g4j2PluginsCacheFileTransformerSpec.groovy | 6 +- 7 files changed, 116 insertions(+), 113 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 2992b7941..7df232de7 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -181,22 +181,22 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat } public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator : com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { - public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;)V - public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V - public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V - public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V - public synthetic fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; public fun canRelocateClass (Ljava/lang/String;)Z public fun canRelocatePath (Ljava/lang/String;)Z public fun exclude (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; - public fun getExcludes ()Lorg/gradle/api/provider/SetProperty; - public fun getIncludes ()Lorg/gradle/api/provider/SetProperty; - public fun getPathPattern ()Lorg/gradle/api/provider/Property; - public fun getPattern ()Lorg/gradle/api/provider/Property; - public fun getRawString ()Lorg/gradle/api/provider/Property; - public fun getShadedPathPattern ()Lorg/gradle/api/provider/Property; - public fun getShadedPattern ()Lorg/gradle/api/provider/Property; + public fun getExcludes ()Ljava/util/Set; + public fun getIncludes ()Ljava/util/Set; + public fun getPathPattern ()Ljava/lang/String; + public fun getPattern ()Ljava/lang/String; + public fun getRawString ()Z + public fun getShadedPathPattern ()Ljava/lang/String; + public fun getShadedPattern ()Ljava/lang/String; public fun include (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index bce32a706..e542af430 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,11 @@ ## [Unreleased] +**Fixed** + +- Revert "Migrate SimpleRelocator to using lazy properties" ([#1052](https://github.com/GradleUp/shadow/pull/1052)) + This fixes the relocation not working in `v9.0.0-beta1`. + ## [v9.0.0-beta1] (2024-11-27) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 12dd9cf4c..15311b17d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -1,11 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation -import com.github.jengelman.gradle.plugins.shadow.internal.property import java.util.regex.Pattern import org.codehaus.plexus.util.SelectorUtils -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.Property -import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional @@ -16,77 +12,82 @@ import org.gradle.api.tasks.Optional * @author Mauro Talevi * @author John Engelman */ -@Suppress("LeakingThis") @CacheableRelocator public open class SimpleRelocator @JvmOverloads constructor( - objectFactory: ObjectFactory, pattern: String?, shadedPattern: String?, includes: List? = null, excludes: List? = null, - rawString: Boolean = false, + private val _rawString: Boolean = false, ) : Relocator { + private val _pattern: String + private val _pathPattern: String + private val _shadedPattern: String + private val _shadedPathPattern: String + private val _includes = mutableSetOf() + private val _excludes = mutableSetOf() + + init { + if (_rawString) { + _pathPattern = pattern.orEmpty() + _shadedPathPattern = shadedPattern.orEmpty() + _pattern = "" // not used for raw string relocator + _shadedPattern = "" // not used for raw string relocator + } else { + if (pattern == null) { + _pattern = "" + _pathPattern = "" + } else { + _pattern = pattern.replace('/', '.') + _pathPattern = pattern.replace('.', '/') + } + if (shadedPattern == null) { + _shadedPattern = "hidden.${_pattern}" + _shadedPathPattern = "hidden/$_pathPattern" + } else { + _shadedPattern = shadedPattern.replace('/', '.') + _shadedPathPattern = shadedPattern.replace('.', '/') + } + } + _includes += normalizePatterns(includes) + _excludes += normalizePatterns(excludes) + } @get:Input @get:Optional - public open val pattern: Property = objectFactory.property() + public open val pattern: String get() = _pattern @get:Input - public open val pathPattern: Property = objectFactory.property() + public open val pathPattern: String get() = _pathPattern @get:Input @get:Optional - public open val shadedPattern: Property = objectFactory.property() + public open val shadedPattern: String get() = _shadedPattern @get:Input - public open val shadedPathPattern: Property = objectFactory.property() + public open val shadedPathPattern: String get() = _shadedPathPattern @get:Input - public open val rawString: Property = objectFactory.property(rawString) + public open val rawString: Boolean get() = _rawString @get:Input - public open val includes: SetProperty = objectFactory.setProperty(String::class.java) + public open val includes: Set get() = _includes @get:Input - public open val excludes: SetProperty = objectFactory.setProperty(String::class.java) - - init { - if (rawString) { - pathPattern.set(pattern.orEmpty()) - shadedPathPattern.set(shadedPattern.orEmpty()) - // Don't need to assign pattern and shadedPattern for raw string relocator - } else { - if (pattern == null) { - this.pattern.set("") - this.pathPattern.set("") - } else { - this.pattern.set(pattern.replace('/', '.')) - this.pathPattern.set(pattern.replace('.', '/')) - } - if (shadedPattern == null) { - this.shadedPattern.set(this.pattern.map { "hidden.$it" }) - this.shadedPathPattern.set(this.pathPattern.map { "hidden/$it" }) - } else { - this.shadedPattern.set(shadedPattern.replace('/', '.')) - this.shadedPathPattern.set(shadedPattern.replace('.', '/')) - } - } - this.includes.addAll(normalizePatterns(includes)) - this.excludes.addAll(normalizePatterns(excludes)) - } + public open val excludes: Set get() = _excludes public open fun include(pattern: String): SimpleRelocator = apply { - includes.addAll(normalizePatterns(listOf(pattern))) + _includes += normalizePatterns(listOf(pattern)) } public open fun exclude(pattern: String): SimpleRelocator = apply { - excludes.addAll(normalizePatterns(listOf(pattern))) + _excludes += normalizePatterns(listOf(pattern)) } override fun canRelocatePath(path: String): Boolean { - if (rawString.get()) return Pattern.compile(pathPattern.get()).matcher(path).find() + if (_rawString) return Pattern.compile(_pathPattern).matcher(path).find() // If string is too short - no need to perform expensive string operations - if (path.length < pathPattern.get().length) return false + if (path.length < _pathPattern.length) return false val adjustedPath = if (path.endsWith(".class")) { // Safeguard against strings containing only ".class" if (path.length == 6) return false @@ -96,60 +97,63 @@ public open class SimpleRelocator @JvmOverloads constructor( } // Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties"). val startIndex = if (adjustedPath.startsWith("/")) 1 else 0 - val pathStartsWithPattern = adjustedPath.startsWith(pathPattern.get(), startIndex) + val pathStartsWithPattern = adjustedPath.startsWith(_pathPattern, startIndex) return pathStartsWithPattern && isIncluded(adjustedPath) && !isExcluded(adjustedPath) } override fun canRelocateClass(className: String): Boolean { - return !rawString.get() && !className.contains('/') && canRelocatePath(className.replace('.', '/')) + return !_rawString && !className.contains('/') && canRelocatePath(className.replace('.', '/')) } override fun relocatePath(context: RelocatePathContext): String { val path = context.path - context.stats.relocate(pathPattern.get(), shadedPathPattern.get()) - return if (rawString.get()) { - path.replace(pathPattern.get().toRegex(), shadedPathPattern.get()) + context.stats.relocate(_pathPattern, _shadedPathPattern) + return if (_rawString) { + path.replace(_pathPattern.toRegex(), _shadedPathPattern) } else { - path.replaceFirst(pathPattern.get(), shadedPathPattern.get()) + path.replaceFirst(_pathPattern, _shadedPathPattern) } } override fun relocateClass(context: RelocateClassContext): String { - context.stats.relocate(pathPattern.get(), shadedPathPattern.get()) - return context.className.replaceFirst(pattern.orNull.orEmpty(), shadedPattern.orNull.orEmpty()) + context.stats.relocate(_pathPattern, _shadedPathPattern) + return context.className.replaceFirst(_pattern, _shadedPattern) } override fun applyToSourceContent(sourceContent: String): String { - return if (rawString.get()) { + return if (_rawString) { sourceContent } else { - sourceContent.replace("\\b${pattern.orNull}".toRegex(), shadedPattern.orNull.orEmpty()) - } - } - - private fun normalizePatterns(patterns: Collection?) = buildSet { - for (pattern in patterns.orEmpty()) { - // Regex patterns don't need to be normalized and stay as is - if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { - add(pattern) - continue - } - - val classPattern = pattern.replace('.', '/') - add(classPattern) - - if (classPattern.endsWith("/*")) { - val packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) - add(packagePattern) - } + sourceContent.replace("\\b$_pattern".toRegex(), _shadedPattern) } } private fun isIncluded(path: String): Boolean { - return includes.get().isEmpty() || includes.get().any { SelectorUtils.matchPath(it, path, "/", true) } + return _includes.isEmpty() || _includes.any { SelectorUtils.matchPath(it, path, "/", true) } } private fun isExcluded(path: String): Boolean { - return excludes.get().any { SelectorUtils.matchPath(it, path, "/", true) } + return _excludes.any { SelectorUtils.matchPath(it, path, "/", true) } + } + + private companion object { + fun normalizePatterns(patterns: Collection?) = buildSet { + patterns ?: return@buildSet + for (pattern in patterns) { + // Regex patterns don't need to be normalized and stay as is + if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { + add(pattern) + continue + } + + val classPattern = pattern.replace('.', '/') + add(classPattern) + + if (classPattern.endsWith("/*")) { + val packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) + add(packagePattern) + } + } + } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index ee9c18ec7..f0a1ab085 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -213,7 +213,7 @@ public abstract class ShadowJar : destination: String, action: Action?, ): ShadowJar = apply { - val relocator = SimpleRelocator(objectFactory, pattern, destination) + val relocator = SimpleRelocator(pattern, destination) addRelocator(relocator, action) } @@ -289,7 +289,7 @@ public abstract class ShadowJar : jarFile.entries().toList() .filter { it.name.endsWith(".class") && it.name != "module-info.class" } .map { it.name.substringBeforeLast('/').replace('/', '.') } - .map { SimpleRelocator(objectFactory, it, "$prefix.$it") } + .map { SimpleRelocator(it, "$prefix.$it") } } } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy index 3cab488f9..9e47404ca 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy @@ -19,7 +19,6 @@ package com.github.jengelman.gradle.plugins.shadow.relocation -import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Test import static org.junit.jupiter.api.Assertions.fail @@ -32,7 +31,6 @@ import static org.junit.jupiter.api.Assertions.fail * @author John Engelman */ class SimpleRelocatorParameterTest { - private static final def objectFactory = ProjectBuilder.builder().build().objects @Test void testThatNullPatternInConstructorShouldNotThrowNullPointerException() { @@ -46,7 +44,7 @@ class SimpleRelocatorParameterTest { private static void constructThenFailOnNullPointerException(String pattern, String shadedPattern) { try { - new SimpleRelocator(objectFactory, pattern, shadedPattern) + new SimpleRelocator(pattern, shadedPattern) } catch (NullPointerException ignored) { fail("Constructor should not throw null pointer exceptions") diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy index 5d0eb0b98..f1488c523 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy @@ -20,7 +20,6 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -39,7 +38,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals */ class SimpleRelocatorTest { - private static final def objectFactory = ProjectBuilder.builder().build().objects private static ShadowStats stats @BeforeEach @@ -51,7 +49,7 @@ class SimpleRelocatorTest { void testCanRelocatePath() { SimpleRelocator relocator - relocator = new SimpleRelocator(objectFactory, "org.foo", null) + relocator = new SimpleRelocator("org.foo", null) assertEquals(true, relocator.canRelocatePath("org/foo/Class")) assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class")) @@ -65,7 +63,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("/org/Foo/Class")) assertEquals(false, relocator.canRelocatePath("/org/Foo/Class.class")) - relocator = new SimpleRelocator(objectFactory, "org.foo", null, null, + relocator = new SimpleRelocator("org.foo", null, null, List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) assertEquals(true, relocator.canRelocatePath("org/foo/Class")) assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) @@ -92,7 +90,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class.class")) // Verify edge cases - relocator = new SimpleRelocator(objectFactory, "org.f", null) + relocator = new SimpleRelocator("org.f", null) assertEquals(false, relocator.canRelocatePath("")) // Empty path assertEquals(false, relocator.canRelocatePath(".class")) // only .class assertEquals(false, relocator.canRelocatePath("te")) // shorter than path pattern @@ -106,7 +104,7 @@ class SimpleRelocatorTest { SimpleRelocator relocator // Include with Regex - relocator = new SimpleRelocator(objectFactory, "org.foo", null, Collections.singletonList("%regex[org/foo/R(\\\$.*)?\$]"), null) + relocator = new SimpleRelocator("org.foo", null, Collections.singletonList("%regex[org/foo/R(\\\$.*)?\$]"), null) assertEquals(true, relocator.canRelocatePath("org/foo/R.class")) assertEquals(true, relocator.canRelocatePath("org/foo/R\$string.class")) assertEquals(true, relocator.canRelocatePath("org/foo/R\$layout.class")) @@ -117,7 +115,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/R\$string.class")) // Exclude with Regex - relocator = new SimpleRelocator(objectFactory, "org.foo", null) + relocator = new SimpleRelocator("org.foo", null) relocator.exclude("%regex[org/foo/.*Factory[0-9].*]") assertEquals(true, relocator.canRelocatePath("org/foo/Factory.class")) assertEquals(true, relocator.canRelocatePath("org/foo/FooFactoryMain.class")) @@ -127,7 +125,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/foo/BarFactory2.class")) // Include with Regex and normal pattern - relocator = new SimpleRelocator(objectFactory, "org.foo", null, + relocator = new SimpleRelocator("org.foo", null, List.of("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*")) assertEquals(true, relocator.canRelocatePath("org/foo/Factory1.class")) assertEquals(true, relocator.canRelocatePath("org/foo/public/Bar.class")) @@ -139,13 +137,13 @@ class SimpleRelocatorTest { void testCanRelocateClass() { SimpleRelocator relocator - relocator = new SimpleRelocator(objectFactory, "org.foo", null) + relocator = new SimpleRelocator("org.foo", null) assertEquals(true, relocator.canRelocateClass("org.foo.Class")) assertEquals(true, relocator.canRelocateClass("org.foo.bar.Class")) assertEquals(false, relocator.canRelocateClass("com.foo.bar.Class")) assertEquals(false, relocator.canRelocateClass("org.Foo.Class")) - relocator = new SimpleRelocator(objectFactory, "org.foo", null, null, + relocator = new SimpleRelocator("org.foo", null, null, List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) assertEquals(true, relocator.canRelocateClass("org.foo.Class")) assertEquals(true, relocator.canRelocateClass("org.foo.excluded")) @@ -168,17 +166,17 @@ class SimpleRelocatorTest { void testCanRelocateRawString() { SimpleRelocator relocator - relocator = new SimpleRelocator(objectFactory, "org/foo", null, null, null, true) + relocator = new SimpleRelocator("org/foo", null, null, null, true) assertEquals(true, relocator.canRelocatePath("(I)org/foo/bar/Class")) - relocator = new SimpleRelocator(objectFactory, "^META-INF/org.foo.xml\$", null, null, null, true) + relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", null, null, null, true) assertEquals(true, relocator.canRelocatePath("META-INF/org.foo.xml")) } //MSHADE-119, make sure that the easy part of this works. @Test void testCanRelocateAbsClassPath() { - SimpleRelocator relocator = new SimpleRelocator(objectFactory, "org.apache.velocity", "org.apache.momentum") + SimpleRelocator relocator = new SimpleRelocator("org.apache.velocity", "org.apache.momentum") assertEquals("/org/apache/momentum/mass.properties", relocator.relocatePath(pathContext("/org/apache/velocity/mass.properties"))) } @@ -187,10 +185,10 @@ class SimpleRelocatorTest { void testRelocatePath() { SimpleRelocator relocator - relocator = new SimpleRelocator(objectFactory, "org.foo", null) + relocator = new SimpleRelocator("org.foo", null) assertEquals("hidden/org/foo/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) - relocator = new SimpleRelocator(objectFactory, "org.foo", "private.stuff") + relocator = new SimpleRelocator("org.foo", "private.stuff") assertEquals("private/stuff/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) } @@ -198,10 +196,10 @@ class SimpleRelocatorTest { void testRelocateClass() { SimpleRelocator relocator - relocator = new SimpleRelocator(objectFactory, "org.foo", null) + relocator = new SimpleRelocator("org.foo", null) assertEquals("hidden.org.foo.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) - relocator = new SimpleRelocator(objectFactory, "org.foo", "private.stuff") + relocator = new SimpleRelocator("org.foo", "private.stuff") assertEquals("private.stuff.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) } @@ -209,10 +207,10 @@ class SimpleRelocatorTest { void testRelocateRawString() { SimpleRelocator relocator - relocator = new SimpleRelocator(objectFactory, "Lorg/foo", "Lhidden/org/foo", null, null, true) + relocator = new SimpleRelocator("Lorg/foo", "Lhidden/org/foo", null, null, true) assertEquals("(I)Lhidden/org/foo/bar/Class", relocator.relocatePath(pathContext("(I)Lorg/foo/bar/Class"))) - relocator = new SimpleRelocator(objectFactory, "^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", null, null, true) + relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", null, null, true) assertEquals("META-INF/hidden.org.foo.xml", relocator.relocatePath(pathContext("META-INF/org.foo.xml"))) } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy index b718f52b5..e48aaf66a 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy @@ -5,7 +5,6 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import org.apache.logging.log4j.core.config.plugins.processor.PluginCache import org.apache.tools.zip.ZipOutputStream -import org.gradle.testfixtures.ProjectBuilder import spock.lang.Specification import static java.util.Collections.singletonList @@ -18,7 +17,6 @@ import static org.apache.logging.log4j.core.config.plugins.processor.PluginProce * @see LinkedIn */ class Log4j2PluginsCacheFileTransformerSpec extends Specification { - private static final def objectFactory = ProjectBuilder.builder().build().objects Log4j2PluginsCacheFileTransformer transformer @@ -37,7 +35,7 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { void "should transform"() { given: List relocators = new ArrayList<>() - relocators.add(new SimpleRelocator(objectFactory, null, null)) + relocators.add(new SimpleRelocator(null, null)) when: transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators)) @@ -51,7 +49,7 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { String pattern = "org.apache.logging" String destination = "new.location.org.apache.logging" - List relocators = singletonList((Relocator) new SimpleRelocator(objectFactory, pattern, destination)) + List relocators = singletonList((Relocator) new SimpleRelocator(pattern, destination)) when: transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators, new ShadowStats())) From a8f514be909839f956bf9bf1ff94b0719f7c8e0d Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 28 Nov 2024 12:13:03 +0800 Subject: [PATCH 042/941] Prepare version 9.0.0-beta2 --- gradle.properties | 2 +- src/docs/changes/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index c64924434..412cce8dd 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.configuration-cache.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta2 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index e542af430..fdd85e04a 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased] + +## [v9.0.0-beta2] (2024-11-28) + **Fixed** - Revert "Migrate SimpleRelocator to using lazy properties" ([#1052](https://github.com/GradleUp/shadow/pull/1052)) @@ -444,7 +447,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Thu, 28 Nov 2024 12:13:53 +0800 Subject: [PATCH 043/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 412cce8dd..c64924434 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.configuration-cache.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta2 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 45b13e577bebb1674aaccf00121b7168cff94faa Mon Sep 17 00:00:00 2001 From: Matthew Haughton <3flex@users.noreply.github.com> Date: Thu, 28 Nov 2024 17:10:57 +1100 Subject: [PATCH 044/941] Fix PR reference number for removal of Develocity integration (#1053) --- 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 fdd85e04a..4fb75cdf8 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -29,7 +29,7 @@ **Removed** -- **BREAKING CHANGE:** Remove Develocity integration. ([#1013](https://github.com/GradleUp/shadow/pull/1013)) +- **BREAKING CHANGE:** Remove Develocity integration. ([#1014](https://github.com/GradleUp/shadow/pull/1014)) **Fixed** From e06ceaf92c181328d3e67a9f672cf173ec8afe7d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 28 Nov 2024 02:43:48 -0500 Subject: [PATCH 045/941] Remove unused test resource jars (#1054) --- src/test/jars/plexus-utils-1.4.1.jar | Bin 188648 -> 0 bytes src/test/jars/test-artifact-1.0-SNAPSHOT.jar | Bin 3115 -> 0 bytes src/test/jars/test-project-1.0-SNAPSHOT.jar | Bin 3906 -> 0 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/test/jars/plexus-utils-1.4.1.jar delete mode 100644 src/test/jars/test-artifact-1.0-SNAPSHOT.jar delete mode 100644 src/test/jars/test-project-1.0-SNAPSHOT.jar diff --git a/src/test/jars/plexus-utils-1.4.1.jar b/src/test/jars/plexus-utils-1.4.1.jar deleted file mode 100644 index 690fc044352971ee7bd3926433cb94449a2fe3db..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 188648 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 !IW@E6xf+p_v8~J2E7! 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 ZiPmb76=Ewli&KF45oX>c2|m`p{{iiGhC2WN From ff11d5a2f3174fc48ef17b98b0b20f953b65725b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 28 Nov 2024 05:29:54 -0500 Subject: [PATCH 046/941] Migrate to version catalog (#1056) Nothing changed. ``` Parallel Configuration Cache is an incubating feature. Reusing configuration cache. > Task :dependencies ------------------------------------------------------------ Root project 'shadow' - Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. ------------------------------------------------------------ runtimeClasspath - Runtime classpath of 'main'. +--- org.jetbrains.kotlin:kotlin-stdlib:2.1.0 | \--- org.jetbrains:annotations:13.0 +--- org.apache.ant:ant:1.10.15 | \--- org.apache.ant:ant-launcher:1.10.15 +--- commons-io:commons-io:2.18.0 +--- org.apache.logging.log4j:log4j-core:2.24.2 | \--- org.apache.logging.log4j:log4j-api:2.24.2 +--- org.ow2.asm:asm-commons:9.7.1 | +--- org.ow2.asm:asm:9.7.1 | \--- org.ow2.asm:asm-tree:9.7.1 | \--- org.ow2.asm:asm:9.7.1 +--- org.vafer:jdependency:2.11 +--- org.jdom:jdom2:2.0.6.1 +--- org.codehaus.plexus:plexus-utils:4.0.2 \--- org.codehaus.plexus:plexus-xml:4.0.4 \--- org.apache.maven:maven-xml-impl:4.0.0-alpha-9 +--- org.apache.maven:maven-api-xml:4.0.0-alpha-9 | \--- org.apache.maven:maven-api-meta:4.0.0-alpha-9 \--- com.fasterxml.woodstox:woodstox-core:6.5.1 \--- org.codehaus.woodstox:stax2-api:4.2.1 (*) - Indicates repeated occurrences of a transitive dependency subtree. Gradle expands transitive dependency subtrees only once per project; repeat occurrences only display the root of the subtree, followed by this annotation. A web-based, searchable dependency report is available by adding the --scan option. BUILD SUCCESSFUL in 327ms 1 actionable task: 1 executed Configuration cache entry reused. ``` --- build.gradle.kts | 40 +++++++++++++++++++-------------------- gradle/libs.versions.toml | 25 ++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 20 deletions(-) create mode 100644 gradle/libs.versions.toml diff --git a/build.gradle.kts b/build.gradle.kts index 1c02867c0..99587de6e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,13 +2,13 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion plugins { - kotlin("jvm") version "2.1.0" + alias(libs.plugins.jetbrains.bcv) + alias(libs.plugins.kotlin) + alias(libs.plugins.android.lint) + alias(libs.plugins.spotless) groovy // Required for Spock tests. id("shadow.convention.publish") id("shadow.convention.deploy") - id("org.jetbrains.kotlinx.binary-compatibility-validator") version "0.16.3" - id("com.android.lint") version "8.7.2" - id("com.diffplug.spotless") version "7.0.0.BETA4" } java { @@ -44,27 +44,27 @@ spotless { } dependencies { - implementation("org.jdom:jdom2:2.0.6.1") - implementation("org.ow2.asm:asm-commons:9.7.1") - implementation("commons-io:commons-io:2.18.0") - implementation("org.apache.ant:ant:1.10.15") - implementation("org.codehaus.plexus:plexus-utils:4.0.2") - implementation("org.codehaus.plexus:plexus-xml:4.0.4") - implementation("org.apache.logging.log4j:log4j-core:2.24.2") - implementation("org.vafer:jdependency:2.11") + implementation(libs.apache.ant) + implementation(libs.apache.commonsIo) + implementation(libs.apache.log4j) + implementation(libs.asm) + implementation(libs.jdependency) + implementation(libs.jdom2) + implementation(libs.plexus.utils) + implementation(libs.plexus.xml) - testImplementation("org.spockframework:spock-core:2.3-groovy-3.0") { + testImplementation(libs.spock) { exclude(group = "org.codehaus.groovy") exclude(group = "org.hamcrest") } - testImplementation("org.xmlunit:xmlunit-legacy:2.10.0") - testImplementation("org.apache.commons:commons-lang3:3.17.0") - testImplementation("com.google.guava:guava:33.3.1-jre") - testImplementation(platform("org.junit:junit-bom:5.11.3")) - testImplementation("org.junit.jupiter:junit-jupiter") - testImplementation("org.junit.platform:junit-platform-suite-engine") + testImplementation(platform(libs.junit.bom)) + testImplementation(libs.junit.jupiter) + testImplementation(libs.junit.platformSuite) + testImplementation(libs.xmlunit) + testImplementation(libs.apache.commonsLang) + testImplementation(libs.guava) - lintChecks("androidx.lint:lint-gradle:1.0.0-alpha02") + lintChecks(libs.androidx.gradlePluginLints) } val isCI = providers.environmentVariable("CI").isPresent diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 000000000..657ed20ba --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,25 @@ +[libraries] +apache-ant = "org.apache.ant:ant:1.10.15" +apache-commonsIo = "commons-io:commons-io:2.18.0" +apache-commonsLang = "org.apache.commons:commons-lang3:3.17.0" +apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.2" +asm = "org.ow2.asm:asm-commons:9.7.1" +guava = "com.google.guava:guava:33.3.1-jre" +jdependency = "org.vafer:jdependency:2.11" +jdom2 = "org.jdom:jdom2:2.0.6.1" +plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" +plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" +xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" + +androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha02" + +spock = "org.spockframework:spock-core:2.3-groovy-3.0" +junit-bom = "org.junit:junit-bom:5.11.3" +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" } +junit-platformSuite = { module = "org.junit.platform:junit-platform-suite-engine" } + +[plugins] +kotlin = "org.jetbrains.kotlin.jvm:2.1.0" +android-lint = "com.android.lint:8.7.2" +jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.16.3" +spotless = "com.diffplug.spotless:7.0.0.BETA4" From 8bd4dc6317e939752017f8d34249ac70d7817e6b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 28 Nov 2024 05:38:15 -0500 Subject: [PATCH 047/941] Track Ktlint updates (#1057) --- build.gradle.kts | 4 ++-- gradle/libs.versions.toml | 2 ++ .../github/jengelman/gradle/plugins/shadow/internal/Utils.kt | 5 +++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 99587de6e..fbb01108b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,10 +34,10 @@ lint { spotless { kotlin { - ktlint() + ktlint(libs.ktlint.get().version) } kotlinGradle { - ktlint() + ktlint(libs.ktlint.get().version) target("**/*.kts") targetExclude("build-logic/build/**") } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 657ed20ba..bc7897da7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,6 +12,8 @@ plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha02" +# Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. +ktlint = "com.pinterest.ktlint:ktlint-cli:1.4.1" spock = "org.spockframework:spock-core:2.3-groovy-3.0" junit-bom = "org.junit:junit-bom:5.11.3" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 2250b1062..dfd019c8e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -22,8 +22,9 @@ internal inline fun ObjectFactory.property(defaultValue: T? = } @Suppress("NOTHING_TO_INLINE") -internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy = - lazy(LazyThreadSafetyMode.NONE, initializer) +internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy { + return lazy(LazyThreadSafetyMode.NONE, initializer) +} internal fun Class<*>.requireResourceAsText(name: String): String { return requireResourceAsStream(name).bufferedReader().readText() From 2888801320e81db0c5d266ed19fac039fa412747 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 28 Nov 2024 18:38:58 +0800 Subject: [PATCH 048/941] Tweaks --- build.gradle.kts | 2 +- src/docs/changes/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index fbb01108b..477a9b999 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,9 +2,9 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion plugins { - alias(libs.plugins.jetbrains.bcv) alias(libs.plugins.kotlin) alias(libs.plugins.android.lint) + alias(libs.plugins.jetbrains.bcv) alias(libs.plugins.spotless) groovy // Required for Spock tests. id("shadow.convention.publish") diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 4fb75cdf8..a54a5d83c 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -23,7 +23,7 @@ - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) - **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) - **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) - `isEnableRelocation` is deprecated, use `enableRelocation` instead. + `isEnableRelocation` is removed, use `enableRelocation` instead. - **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) - **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) From 6d03a5dcde798613db3ade048443633ccb3369fb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 29 Nov 2024 19:07:23 -0500 Subject: [PATCH 049/941] Add integrationTest task and migrate doc tests to Kotlin (#1055) * Rearrange * Migrate SnippetExecutor * Migrate NoopExecutor * Migrate CompileException * Migrate GradleBuildExecutor * Migrate ManualSnippetExtractor * Migrate GroovyDslFixture * Migrate GroovyScriptFixture and SnippetFixture * Migrate TestCodeSnippet * Migrate ExceptionTransformer * Migrate ManualCodeSnippetTests * Fix typo in package * Add integrationTest task and move sources * Remove guava and tweak deps * Fix and cleanups * Split packages * Fix and remove outdated logic * Remove CompileException * Use createTempDirectory * Rename ManualCodeSnippetTests * Optimize relativeDocPath * Replace arguments * Fix * Add an unknown property for testing * Better stacktrace transforming in ExceptionTransformer * Simplify SnippetExecutor * Rename classes * Remove ExceptionTransformer * Improve error line message * Revert "Add an unknown property for testing" This reverts commit f722bb7086ee56b58d7ca335fb9289e8c6cc30d9. * Extends testImplementation and testRuntimeOnly * Remove unused junit-platform-suite-engine * Mark more blocks no-run * MarK GroovyDslFixture as an object * Simplify SnippetFixture * Rearrange packages * Rename --- build.gradle.kts | 33 ++++++- gradle/libs.versions.toml | 3 +- src/docs/getting-started/README.md | 2 +- src/docs/plugins/README.md | 2 +- .../plugins/shadow/doc/DocCodeSnippetTest.kt | 35 +++++++ .../doc/executable/CodeSnippetExecutable.kt | 23 +++++ .../doc/executable/CodeSnippetExtractor.kt | 92 +++++++++++++++++++ .../doc/executor/GroovyBuildExecutor.kt | 63 +++++++++++++ .../shadow/doc/executor/NoopExecutor.kt | 9 ++ .../shadow/doc/executor/SnippetExecutor.kt | 10 ++ .../shadow/doc/fixture/GroovyDslFixture.kt | 38 ++++++++ .../shadow/doc/fixture/SnippetFixture.kt | 7 ++ .../shadow/docs/ManualCodeSnippetTests.groovy | 39 -------- .../docs/executer/GradleBuildExecuter.groovy | 83 ----------------- .../shadow/docs/executer/NoopExecuter.groovy | 18 ---- .../extractor/ManualSnippetExtractor.groovy | 68 -------------- .../docs/fixture/GroovyDslFixture.groovy | 49 ---------- .../plugins/shadow/docs/internal/Block.java | 11 --- .../internal/snippets/TestCodeSnippet.java | 40 -------- .../snippets/executer/CompileException.java | 18 ---- .../executer/ExceptionTransformer.groovy | 39 -------- .../snippets/executer/SnippetExecuter.java | 14 --- .../fixture/GroovyScriptFixture.groovy | 10 -- .../snippets/fixture/SnippetFixture.java | 27 ------ 24 files changed, 309 insertions(+), 424 deletions(-) create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/ManualCodeSnippetTests.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/executer/GradleBuildExecuter.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/executer/NoopExecuter.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/extractor/ManualSnippetExtractor.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/fixture/GroovyDslFixture.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/Block.java delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/TestCodeSnippet.java delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/CompileException.java delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/ExceptionTransformer.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/SnippetExecuter.java delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/GroovyScriptFixture.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/SnippetFixture.java diff --git a/build.gradle.kts b/build.gradle.kts index 477a9b999..990ad1a41 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,6 +43,18 @@ spotless { } } +val integrationTestSourceSet: SourceSet = sourceSets.create("integrationTest") +val integrationTestImplementation: Configuration by configurations.getting { + extendsFrom(configurations.testImplementation.get()) +} +val integrationTestRuntimeOnly: Configuration by configurations.getting { + extendsFrom(configurations.testRuntimeOnly.get()) +} + +gradlePlugin { + testSourceSets.add(integrationTestSourceSet) +} + dependencies { implementation(libs.apache.ant) implementation(libs.apache.commonsIo) @@ -59,14 +71,29 @@ dependencies { } testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) - testImplementation(libs.junit.platformSuite) testImplementation(libs.xmlunit) testImplementation(libs.apache.commonsLang) - testImplementation(libs.guava) + testRuntimeOnly(libs.junit.platformLauncher) lintChecks(libs.androidx.gradlePluginLints) } +val integrationTest by tasks.registering(Test::class) { + description = "Runs the integration tests." + group = LifecycleBasePlugin.VERIFICATION_GROUP + testClassesDirs = integrationTestSourceSet.output.classesDirs + classpath = integrationTestSourceSet.runtimeClasspath + + val docsDir = file("src/docs") + // Add src/docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. + inputs.dir(docsDir) + systemProperty("DOCS_DIR", docsDir.absolutePath) +} + +tasks.check { + dependsOn(integrationTest) +} + val isCI = providers.environmentVariable("CI").isPresent tasks.withType().configureEach { @@ -80,8 +107,6 @@ tasks.withType().configureEach { maxHeapSize = "1g" } - // Add src/docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. - inputs.dir(file("src/docs")) systemProperty("shadowVersion", version) // Required to test configuration cache in tests when using withDebug() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bc7897da7..7d3d1d228 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,6 @@ apache-commonsIo = "commons-io:commons-io:2.18.0" apache-commonsLang = "org.apache.commons:commons-lang3:3.17.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.2" asm = "org.ow2.asm:asm-commons:9.7.1" -guava = "com.google.guava:guava:33.3.1-jre" jdependency = "org.vafer:jdependency:2.11" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" @@ -18,7 +17,7 @@ ktlint = "com.pinterest.ktlint:ktlint-cli:1.4.1" spock = "org.spockframework:spock-core:2.3-groovy-3.0" junit-bom = "org.junit:junit-bom:5.11.3" junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" } -junit-platformSuite = { module = "org.junit.platform:junit-platform-suite-engine" } +junit-platformLauncher = { module = "org.junit.platform:junit-platform-launcher" } [plugins] kotlin = "org.jetbrains.kotlin.jvm:2.1.0" diff --git a/src/docs/getting-started/README.md b/src/docs/getting-started/README.md index 7c5726f5b..8ad57fdcc 100644 --- a/src/docs/getting-started/README.md +++ b/src/docs/getting-started/README.md @@ -1,6 +1,6 @@ # Getting Started -```groovy no-plugins +```groovy no-run plugins { id 'com.gradleup.shadow' version '@version@' id 'java' diff --git a/src/docs/plugins/README.md b/src/docs/plugins/README.md index 81bccc274..797e0a2c8 100644 --- a/src/docs/plugins/README.md +++ b/src/docs/plugins/README.md @@ -10,7 +10,7 @@ and `relocationPrefix` settings on any `ShadowJar` task. A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the `shadowJar` task for relocation. -```groovy no-plugins +```groovy no-run plugins { id 'com.gradleup.shadow' version '@version@' id 'java' diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt new file mode 100644 index 000000000..887c4a273 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt @@ -0,0 +1,35 @@ +package com.github.jengelman.gradle.plugins.shadow.doc + +import com.github.jengelman.gradle.plugins.shadow.doc.executable.CodeSnippetExtractor +import com.github.jengelman.gradle.plugins.shadow.doc.executor.GroovyBuildExecutor +import com.github.jengelman.gradle.plugins.shadow.doc.executor.NoopExecutor +import com.github.jengelman.gradle.plugins.shadow.doc.fixture.GroovyDslFixture +import java.nio.file.Path +import kotlin.io.path.Path +import org.junit.jupiter.api.DynamicTest +import org.junit.jupiter.api.TestFactory +import org.junit.jupiter.api.io.TempDir + +class DocCodeSnippetTest { + + @TestFactory + fun provideDynamicTests(@TempDir tempDir: Path): List { + return fixtures.flatMap { (selector, executor) -> + CodeSnippetExtractor.extract(tempDir, docsDir, selector, executor) + }.map { + DynamicTest.dynamicTest(it.testName, it) + } + } + + companion object { + private val fixtures = mapOf( + "groovy" to GroovyBuildExecutor( + GroovyDslFixture, + GroovyDslFixture.importsExtractor, + ), + "groovy no-run" to NoopExecutor, + ) + + val docsDir: Path = Path(System.getProperty("DOCS_DIR")) + } +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt new file mode 100644 index 000000000..7a734b6b3 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt @@ -0,0 +1,23 @@ +package com.github.jengelman.gradle.plugins.shadow.doc.executable + +import com.github.jengelman.gradle.plugins.shadow.doc.executor.SnippetExecutor +import java.nio.file.Path +import kotlin.io.path.createTempDirectory +import org.junit.jupiter.api.function.Executable + +class CodeSnippetExecutable( + private val tempDir: Path, + private val snippet: String, + val testName: String, + private val executor: SnippetExecutor, + private val exceptionTransformer: (Throwable) -> Throwable, +) : Executable { + override fun execute() { + try { + // TODO: any way to createTempDirectory with `@TempDir` for each `Executable`? + executor.execute(createTempDirectory(tempDir, "doc-"), snippet) + } catch (t: Throwable) { + throw exceptionTransformer(t) + } + } +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt new file mode 100644 index 000000000..334cdee26 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt @@ -0,0 +1,92 @@ +package com.github.jengelman.gradle.plugins.shadow.doc.executable + +import com.github.jengelman.gradle.plugins.shadow.doc.DocCodeSnippetTest +import com.github.jengelman.gradle.plugins.shadow.doc.executor.SnippetExecutor +import java.nio.file.Path +import java.util.regex.Pattern +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.name +import kotlin.io.path.pathString +import kotlin.io.path.readText +import kotlin.io.path.relativeTo +import kotlin.io.path.walk + +object CodeSnippetExtractor { + fun extract( + tempDir: Path, + docRoot: Path, + cssClass: String, + executor: SnippetExecutor, + ): List { + val snippets = mutableListOf() + val snippetBlockPattern = Pattern.compile("(?ims)```$cssClass\n(.*?)\n```") + @OptIn(ExperimentalPathApi::class) + docRoot.walk() + .filter { it.name.endsWith(".md", ignoreCase = true) } + .forEach { + addSnippets(tempDir, snippets, it, snippetBlockPattern, executor) + } + return snippets + } + + private fun addSnippets( + tempDir: Path, + snippets: MutableList, + path: Path, + snippetBlockPattern: Pattern, + executor: SnippetExecutor, + ) { + val source = path.readText() + val relativeDocPath = path.relativeTo(DocCodeSnippetTest.docsDir).pathString + val snippetsByLine = findSnippetsByLine(source, snippetBlockPattern) + + snippetsByLine.forEach { (lineNumber, snippet) -> + snippets.add(createSnippet(tempDir, relativeDocPath, path, lineNumber, snippet, executor)) + } + } + + private fun findSnippetBlocks(code: String, snippetTagPattern: Pattern) = buildList { + val matcher = snippetTagPattern.matcher(code) + while (matcher.find()) { + add(matcher.group(0)) + } + } + + private fun findSnippetsByLine(source: String, snippetTagPattern: Pattern): Map { + val snippetBlocks = findSnippetBlocks(source, snippetTagPattern) + val snippetBlocksByLine = mutableMapOf() + + var codeIndex = 0 + snippetBlocks.forEach { block -> + codeIndex = source.indexOf(block, codeIndex) + val lineNumber = source.substring(0, codeIndex).lines().size + 1 + snippetBlocksByLine[lineNumber] = extractSnippetFromBlock(block) + codeIndex += block.length + } + + return snippetBlocksByLine + } + + private fun extractSnippetFromBlock(tag: String): String { + return tag.substring(tag.indexOf("\n") + 1, tag.lastIndexOf("\n")) + } + + private fun createSnippet( + tempDir: Path, + sourceClassName: String, + sourcePath: Path, + lineNumber: Int, + snippet: String, + executor: SnippetExecutor, + ): CodeSnippetExecutable { + return CodeSnippetExecutable( + tempDir, + snippet, + "$sourceClassName:$lineNumber", + executor, + ) { + val message = "The error line in the doc is near ${sourcePath.toUri()}:$lineNumber" + RuntimeException(message, it) + } + } +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt new file mode 100644 index 000000000..68c2dc7e2 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt @@ -0,0 +1,63 @@ +package com.github.jengelman.gradle.plugins.shadow.doc.executor + +import com.github.jengelman.gradle.plugins.shadow.doc.fixture.SnippetFixture +import java.nio.file.Path +import kotlin.io.path.createDirectory +import kotlin.io.path.writeText +import org.gradle.testkit.runner.GradleRunner +import org.intellij.lang.annotations.Language + +class GroovyBuildExecutor( + override val fixture: SnippetFixture, + private val importExtractor: (String) -> List, + private val arguments: Array = arrayOf("build", "-m"), +) : SnippetExecutor { + + override fun execute(tempDir: Path, snippet: String) { + tempDir.resolve("settings.gradle").writeText(settingsBuildText) + tempDir.addSubProject("api", projectBuildText) + + val (imports, snippetWithoutImports) = importExtractor(snippet) + val fullSnippet = imports + fixture.pre + '\n' + snippetWithoutImports + '\n' + fixture.post + + tempDir.addSubProject("main", fullSnippet) + + try { + GradleRunner.create() + .withProjectDir(tempDir.toFile()) + .withPluginClasspath() + .forwardOutput() + .withArguments(*arguments) + .build() + } catch (t: Throwable) { + throw RuntimeException("Failed to execute snippet:\n\n$fullSnippet", t) + } + } + + private fun Path.addSubProject(project: String, buildScriptText: String) { + resolve(project) + .createDirectory() + .resolve("build.gradle") + .writeText(buildScriptText) + } + + private companion object { + @Language("Groovy") + private val settingsBuildText = """ + rootProject.name = 'shadowTest' + include 'api', 'main' + """.trimIndent() + + @Language("Groovy") + private val projectBuildText = """ + plugins { + id 'java' + id 'com.gradleup.shadow' + } + repositories { + mavenLocal() + mavenCentral() + } + """.trimIndent() + } +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt new file mode 100644 index 000000000..3820b8aef --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt @@ -0,0 +1,9 @@ +package com.github.jengelman.gradle.plugins.shadow.doc.executor + +import com.github.jengelman.gradle.plugins.shadow.doc.fixture.SnippetFixture +import java.nio.file.Path + +object NoopExecutor : SnippetExecutor { + override val fixture: SnippetFixture get() = error("NoopExecutor does not have a fixture.") + override fun execute(tempDir: Path, snippet: String) = Unit +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt new file mode 100644 index 000000000..09f87c315 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt @@ -0,0 +1,10 @@ +package com.github.jengelman.gradle.plugins.shadow.doc.executor + +import com.github.jengelman.gradle.plugins.shadow.doc.fixture.SnippetFixture +import java.nio.file.Path + +interface SnippetExecutor { + val fixture: SnippetFixture + + fun execute(tempDir: Path, snippet: String) +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt new file mode 100644 index 000000000..b4e60e4c3 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt @@ -0,0 +1,38 @@ +package com.github.jengelman.gradle.plugins.shadow.doc.fixture + +import org.intellij.lang.annotations.Language + +object GroovyDslFixture : SnippetFixture { + + @Language("Groovy") + override val pre: String = """ + plugins { + id("java") + id("com.gradleup.shadow") + id("application") + } + repositories { + mavenLocal() + mavenCentral() + } + version = "1.0" + group = "shadow" + """.trimIndent() + + override val post: String = "" + + val importsExtractor: (String) -> List = { snippet -> + val imports = StringBuilder() + val scriptMinusImports = StringBuilder() + + snippet.lines().forEach { line -> + val target = if (line.trim().startsWith("import ")) imports else scriptMinusImports + target.append(line).append("\n") + } + + listOf( + imports.toString(), + scriptMinusImports.toString(), + ) + } +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt new file mode 100644 index 000000000..a554ad205 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt @@ -0,0 +1,7 @@ +package com.github.jengelman.gradle.plugins.shadow.doc.fixture + +interface SnippetFixture { + val pre: String + + val post: String +} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/ManualCodeSnippetTests.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/ManualCodeSnippetTests.groovy deleted file mode 100644 index e56470122..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/ManualCodeSnippetTests.groovy +++ /dev/null @@ -1,39 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs - -import com.github.jengelman.gradle.plugins.shadow.docs.executer.GradleBuildExecuter -import com.github.jengelman.gradle.plugins.shadow.docs.executer.NoopExecuter -import com.github.jengelman.gradle.plugins.shadow.docs.extractor.ManualSnippetExtractor -import com.github.jengelman.gradle.plugins.shadow.docs.fixture.GroovyDslFixture -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.TestCodeSnippet -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer.SnippetExecuter -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixture.GroovyScriptFixture -import com.google.common.base.StandardSystemProperty -import org.junit.jupiter.api.DynamicTest -import org.junit.jupiter.api.TestFactory -import org.junit.jupiter.api.io.TempDir - -import java.nio.file.Path - -class ManualCodeSnippetTests { - public static final LinkedHashMap FIXTURES = [ - "groovy" : new GradleBuildExecuter("build.gradle", new GroovyDslFixture(), new GroovyDslFixture.ImportsExtractor()), - "groovy no-plugins": new GradleBuildExecuter("build.gradle", new GroovyScriptFixture(), new GroovyDslFixture.ImportsExtractor()), - "groovy no-run" : new NoopExecuter() - ] - - @TestFactory - List provideDynamicTests(@TempDir Path tempDir) { - File cwd = new File(StandardSystemProperty.USER_DIR.value()) - def content = new File(cwd, "src/docs") - List snippets = [] - - FIXTURES.each { selector, executer -> - ManualSnippetExtractor.extract(tempDir, content, selector, executer).each { - snippets.add(it) - } - } - return snippets.collect { - DynamicTest.dynamicTest(it.testName, it) - } - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/executer/GradleBuildExecuter.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/executer/GradleBuildExecuter.groovy deleted file mode 100644 index 5e5bfd63e..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/executer/GradleBuildExecuter.groovy +++ /dev/null @@ -1,83 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.executer - -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.TestCodeSnippet -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer.SnippetExecuter -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixture.SnippetFixture -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification -import org.gradle.testkit.runner.GradleRunner - -import java.util.function.Function - -class GradleBuildExecuter implements SnippetExecuter { - - private final SnippetFixture fixture - private final String buildFile - private final Function> importExtractor - - private List arguments = ["build", "-m"] - - GradleBuildExecuter(String buildFile, SnippetFixture fixture, Function> importExtractor) { - this.buildFile = buildFile - this.fixture = fixture - this.importExtractor = importExtractor - } - - GradleBuildExecuter(String buildFile, List arguments, SnippetFixture fixture, Function> importExtractor) { - this(buildFile, fixture, importExtractor) - this.arguments = arguments - } - - @Override - SnippetFixture getFixture() { - return fixture - } - - @Override - void execute(File tempDir, TestCodeSnippet snippet) throws Exception { - addSubProject(tempDir) - File settings = new File(tempDir, "settings.gradle") - settings.text = """ -rootProject.name = 'shadowTest' -include 'api', 'main' -""" - - File mainDir = new File(tempDir, "main") - mainDir.mkdirs() - File buildFile = new File(mainDir, buildFile) - - - List importsAndSnippet = importExtractor.apply(snippet.getSnippet()) - - String imports = importsAndSnippet.get(0) - String snippetMinusImports = fixture.transform(importsAndSnippet.get(1)) - String fullSnippet = imports + fixture.pre() + snippetMinusImports + fixture.post() - - buildFile.text = replaceTokens(fullSnippet) - - GradleRunner runner = GradleRunner.create().withProjectDir(tempDir).withPluginClasspath().forwardOutput() - - runner.withArguments(":main:build", "-m").build() - - } - - private static void addSubProject(File dir) { - File api = new File(dir, "api") - api.mkdirs() - File build = new File(api, "build.gradle") - build.text = """ -plugins { - id 'java' - id 'com.gradleup.shadow' -} - -repositories { - mavenLocal() - mavenCentral() -} -""" - } - - private static String replaceTokens(String snippet) { - return snippet.replaceAll("@version@", PluginSpecification.SHADOW_VERSION + '-SNAPSHOT') - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/executer/NoopExecuter.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/executer/NoopExecuter.groovy deleted file mode 100644 index aabd60c81..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/executer/NoopExecuter.groovy +++ /dev/null @@ -1,18 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.executer - -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.TestCodeSnippet -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer.SnippetExecuter -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixture.SnippetFixture - -class NoopExecuter implements SnippetExecuter { - - @Override - SnippetFixture getFixture() { - return null - } - - @Override - void execute(File tempDir, TestCodeSnippet snippet) throws Exception { - // noop - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/extractor/ManualSnippetExtractor.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/extractor/ManualSnippetExtractor.groovy deleted file mode 100644 index 7f1f85aa1..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/extractor/ManualSnippetExtractor.groovy +++ /dev/null @@ -1,68 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.extractor - -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.TestCodeSnippet -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer.ExceptionTransformer -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer.SnippetExecuter -import groovy.ant.FileNameFinder - -import java.nio.file.Path -import java.util.regex.Pattern - -class ManualSnippetExtractor { - - static List extract(Path tempDir, File root, String cssClass, SnippetExecuter executer) { - List snippets = [] - - def snippetBlockPattern = Pattern.compile(/(?ims)```$cssClass\n(.*?)\n```/) - def filenames = new FileNameFinder().getFileNames(root.absolutePath, "**/*.md") - - filenames.each { filename -> - def file = new File(filename) - addSnippets(tempDir, snippets, file, snippetBlockPattern, executer) - } - - snippets - } - - private static void addSnippets(Path tempDir, List snippets, File file, Pattern snippetBlockPattern, SnippetExecuter executer) { - def source = file.text - String testName = file.parentFile.name + "/" + file.name - Map snippetsByLine = findSnippetsByLine(source, snippetBlockPattern) - - snippetsByLine.each { lineNumber, snippet -> - snippets << createSnippet(tempDir, testName, file, lineNumber, snippet, executer) - } - } - - private static List findSnippetBlocks(String code, Pattern snippetTagPattern) { - List tags = [] - code.eachMatch(snippetTagPattern) { matches -> - tags.add(matches[0]) - } - tags - } - - private static Map findSnippetsByLine(String source, Pattern snippetTagPattern) { - List snippetBlocks = findSnippetBlocks(source, snippetTagPattern) - Map snippetBlocksByLine = [:] - - int codeIndex = 0 - snippetBlocks.each { block -> - codeIndex = source.indexOf(block, codeIndex) - def lineNumber = source.substring(0, codeIndex).readLines().size() + 2 - snippetBlocksByLine.put(lineNumber, extractSnippetFromBlock(block)) - codeIndex += block.size() - } - - snippetBlocksByLine - } - - private static String extractSnippetFromBlock(String tag) { - tag.substring(tag.indexOf("\n") + 1, tag.lastIndexOf("\n")) - } - - private static TestCodeSnippet createSnippet(Path tempDir, String sourceClassName, File sourceFile, int lineNumber, String snippet, SnippetExecuter executer) { - new TestCodeSnippet(tempDir, snippet, sourceClassName + ":$lineNumber", executer, new ExceptionTransformer(sourceClassName, sourceFile.name, lineNumber)) - } - -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/fixture/GroovyDslFixture.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/fixture/GroovyDslFixture.groovy deleted file mode 100644 index 0c67cf501..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/fixture/GroovyDslFixture.groovy +++ /dev/null @@ -1,49 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.fixture - -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixture.GroovyScriptFixture - -import java.util.function.Function - -class GroovyDslFixture extends GroovyScriptFixture { - - @Override - String pre() { - """ -plugins { - id 'java' - id 'com.gradleup.shadow' - id 'application' -} - -version = "1.0" -group = 'shadow' - -repositories { - mavenLocal() - mavenCentral() -} -""" - } - - static class ImportsExtractor implements Function> { - - @Override - List apply(String snippet) { - StringBuilder imports = new StringBuilder() - StringBuilder scriptMinusImports = new StringBuilder() - - for (String line : snippet.split("\\n")) { - StringBuilder target - if (line.trim().startsWith("import ")) { - target = imports - } else { - target = scriptMinusImports - } - - target.append(line).append("\n") - } - - return Arrays.asList(imports.toString(), scriptMinusImports.toString()) - } - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/Block.java b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/Block.java deleted file mode 100644 index 6be5676e3..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/Block.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.internal; - -public interface Block { - - /** - * Execute the action. - * - * @throws Exception any - */ - void execute() throws Exception; -} \ No newline at end of file diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/TestCodeSnippet.java b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/TestCodeSnippet.java deleted file mode 100644 index e3539c1cb..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/TestCodeSnippet.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets; - -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer.ExceptionTransformer; -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer.SnippetExecuter; -import org.junit.jupiter.api.function.Executable; - -import java.nio.file.Path; - -public class TestCodeSnippet implements Executable { - - private final Path tempDir; - private final String snippet; - private final String testName; - private final SnippetExecuter executer; - private final ExceptionTransformer exceptionTransformer; - - public TestCodeSnippet(Path tempDir, String snippet, String testName, SnippetExecuter executer, ExceptionTransformer exceptionTransformer) { - this.tempDir = tempDir; - this.snippet = snippet; - this.testName = testName; - this.executer = executer; - this.exceptionTransformer = exceptionTransformer; - } - - public String getSnippet() { - return snippet; - } - - public String getTestName() { - return testName; - } - - public void execute() throws Throwable { - try { - executer.execute(tempDir.toFile(), this); - } catch (Throwable t) { - throw exceptionTransformer.transform(t, executer.getFixture().getOffset()); - } - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/CompileException.java b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/CompileException.java deleted file mode 100644 index 90768b3f3..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/CompileException.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer; - -public class CompileException extends RuntimeException { - - private static final long serialVersionUID = 0; - - private final int lineNo; - - public CompileException(Throwable cause, int lineNo) { - super(cause); - this.lineNo = lineNo; - } - - public int getLineNo() { - return lineNo; - } - -} \ No newline at end of file diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/ExceptionTransformer.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/ExceptionTransformer.groovy deleted file mode 100644 index 15331d0ef..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/ExceptionTransformer.groovy +++ /dev/null @@ -1,39 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer - -class ExceptionTransformer { - - final String sourceClassName - final String sourceFileName - final Integer lineNumber - - ExceptionTransformer(String sourceClassName, String sourceFileName, Integer lineNumber) { - this.sourceClassName = sourceClassName - this.sourceFileName = sourceFileName - this.lineNumber = lineNumber - } - - Throwable transform(Throwable throwable, Integer offset) throws Exception { - def errorLine = 0 - - if (throwable instanceof CompileException) { - errorLine = throwable.lineNo - } else { - def frame = throwable.getStackTrace().find { it.fileName == sourceClassName } - if (frame) { - errorLine = frame.lineNumber - } else { - frame = throwable.getStackTrace().find { it.fileName == "Example.java" } - if (frame) { - errorLine = frame.lineNumber - } - } - } - errorLine = errorLine - offset - StackTraceElement[] stack = throwable.getStackTrace() - List newStack = new ArrayList(stack.length + 1) - newStack.add(new StackTraceElement(sourceClassName, "javadoc", sourceFileName, lineNumber + errorLine)) - newStack.addAll(stack) - throwable.setStackTrace(newStack as StackTraceElement[]) - throwable - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/SnippetExecuter.java b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/SnippetExecuter.java deleted file mode 100644 index a988861a3..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/executer/SnippetExecuter.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.executer; - -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.TestCodeSnippet; -import com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixture.SnippetFixture; - -import java.io.File; - -public interface SnippetExecuter { - - SnippetFixture getFixture(); - - void execute(File tempDir, TestCodeSnippet snippet) throws Exception; - -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/GroovyScriptFixture.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/GroovyScriptFixture.groovy deleted file mode 100644 index 04fdccd67..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/GroovyScriptFixture.groovy +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixture - -class GroovyScriptFixture extends SnippetFixture { - - @Override - String post() { - "\n;0;" - } - -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/SnippetFixture.java b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/SnippetFixture.java deleted file mode 100644 index b59402d40..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/docs/internal/snippets/fixture/SnippetFixture.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.docs.internal.snippets.fixture; - -import com.github.jengelman.gradle.plugins.shadow.docs.internal.Block; - -public class SnippetFixture { - - public void around(Block action) throws Exception { - action.execute(); - } - - public String transform(String text) { - return text; - } - - public String pre() { - return ""; - } - - public String post() { - return ""; - } - - public Integer getOffset() { - return pre().split("\n").length; - } - -} From 95386fe865fdfbb74c5addffee668e68090fdf88 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 02:25:19 -0500 Subject: [PATCH 050/941] Tweak test options (#1062) --- build.gradle.kts | 11 ----------- .../plugins/shadow/util/PluginSpecification.groovy | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 990ad1a41..28bef5470 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -94,21 +94,10 @@ tasks.check { dependsOn(integrationTest) } -val isCI = providers.environmentVariable("CI").isPresent - tasks.withType().configureEach { useJUnitPlatform() - maxParallelForks = Runtime.getRuntime().availableProcessors() - if (isCI) { - testLogging.showStandardStreams = true - minHeapSize = "1g" - maxHeapSize = "1g" - } - - systemProperty("shadowVersion", version) - // Required to test configuration cache in tests when using withDebug() // https://github.com/gradle/gradle/issues/22765#issuecomment-1339427241 jvmArgs( diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy index 2ad50540a..96f2c459a 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy @@ -40,7 +40,7 @@ abstract class PluginSpecification extends Specification { return """ plugins { id '${javaPlugin}' - id 'com.gradleup.shadow' version '${SHADOW_VERSION}' + id 'com.gradleup.shadow' } version = "1.0" From c63861d0120b81668afd1ee851fe3c0c1871f2e4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 02:31:31 -0500 Subject: [PATCH 051/941] Split tests into unit tests and functional tests (#1061) * Rename integrationTest SourceSet * Add functionalTest configuration * Split source sets * funcTestImplementation depends on spock only --- build.gradle.kts | 40 +++++++++++++----- .../plugins/shadow/ApplicationSpec.groovy | 0 .../shadow/ConfigurationCacheSpec.groovy | 0 .../ConfigureShadowRelocationSpec.groovy | 0 .../plugins/shadow/FilteringSpec.groovy | 0 .../plugins/shadow/PublishingSpec.groovy | 0 .../plugins/shadow/RelocationSpec.groovy | 0 .../plugins/shadow/ShadowPluginSpec.groovy | 0 .../plugins/shadow/TransformerSpec.groovy | 0 .../shadow/caching/AbstractCachingSpec.groovy | 0 .../caching/MinimizationCachingSpec.groovy | 0 .../caching/RelocationCachingSpec.groovy | 0 .../caching/ShadowJarCachingSpec.groovy | 0 .../caching/TransformCachingSpec.groovy | 0 ...g4j2PluginsCacheFileTransformerSpec.groovy | 0 .../PropertiesFileTransformerSpec.groovy | 0 .../ServiceFileTransformerSpec.groovy | 0 .../TransformerSpecSupport.groovy | 0 .../plugins/shadow/util/AppendableJar.groovy | 0 .../util/AppendableMavenFileModule.groovy | 0 .../util/AppendableMavenFileRepository.groovy | 0 .../gradle/plugins/shadow/util/HashUtil.java | 0 .../gradle/plugins/shadow/util/HashValue.java | 0 .../plugins/shadow/util/JarBuilder.groovy | 0 .../shadow/util/PluginSpecification.groovy | 0 .../shadow/util/file/ExecOutput.groovy | 0 .../plugins/shadow/util/file/TestFile.java | 0 .../shadow/util/file/TestFileHelper.groovy | 0 .../util/file/TestWorkspaceBuilder.groovy | 0 .../shadow/util/repo/AbstractModule.groovy | 0 .../repo/maven/AbstractMavenModule.groovy | 0 .../repo/maven/DefaultMavenMetaData.groovy | 0 .../util/repo/maven/MavenDependency.groovy | 0 .../util/repo/maven/MavenFileModule.groovy | 0 .../repo/maven/MavenFileRepository.groovy | 0 .../util/repo/maven/MavenMetaData.groovy | 0 .../shadow/util/repo/maven/MavenModule.groovy | 0 .../shadow/util/repo/maven/MavenPom.groovy | 0 .../util/repo/maven/MavenRepository.groovy | 0 .../shadow/util/repo/maven/MavenScope.groovy | 0 .../resources/junit-3.8.2.jar | Bin .../resources/test-artifact-1.0-SNAPSHOT.jar | Bin .../resources/test-project-1.0-SNAPSHOT.jar | Bin .../plugins/shadow/doc/DocCodeSnippetTest.kt | 0 .../doc/executable/CodeSnippetExecutable.kt | 0 .../doc/executable/CodeSnippetExtractor.kt | 0 .../doc/executor/GroovyBuildExecutor.kt | 0 .../shadow/doc/executor/NoopExecutor.kt | 0 .../shadow/doc/executor/SnippetExecutor.kt | 0 .../shadow/doc/fixture/GroovyDslFixture.kt | 0 .../shadow/doc/fixture/SnippetFixture.kt | 0 51 files changed, 29 insertions(+), 11 deletions(-) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/ExecOutput.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenDependency.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenMetaData.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy (100%) rename src/{test => funcTest}/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenScope.groovy (100%) rename src/{test => funcTest}/resources/junit-3.8.2.jar (100%) rename src/{test => funcTest}/resources/test-artifact-1.0-SNAPSHOT.jar (100%) rename src/{test => funcTest}/resources/test-project-1.0-SNAPSHOT.jar (100%) rename src/{integrationTest => intiTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt (100%) rename src/{integrationTest => intiTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt (100%) rename src/{integrationTest => intiTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt (100%) rename src/{integrationTest => intiTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt (100%) rename src/{integrationTest => intiTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt (100%) rename src/{integrationTest => intiTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt (100%) rename src/{integrationTest => intiTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt (100%) rename src/{integrationTest => intiTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 28bef5470..82be39fcb 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,16 +43,25 @@ spotless { } } -val integrationTestSourceSet: SourceSet = sourceSets.create("integrationTest") -val integrationTestImplementation: Configuration by configurations.getting { +val intiTest: SourceSet by sourceSets.creating +val intiTestImplementation: Configuration by configurations.getting { extendsFrom(configurations.testImplementation.get()) } -val integrationTestRuntimeOnly: Configuration by configurations.getting { +val intiTestRuntimeOnly: Configuration by configurations.getting { + extendsFrom(configurations.testRuntimeOnly.get()) +} + +val funcTest: SourceSet by sourceSets.creating +val funcTestImplementation: Configuration by configurations.getting { + extendsFrom(configurations.testImplementation.get()) +} +val funcTestRuntimeOnly: Configuration by configurations.getting { extendsFrom(configurations.testRuntimeOnly.get()) } gradlePlugin { - testSourceSets.add(integrationTestSourceSet) + testSourceSets.add(intiTest) + testSourceSets.add(funcTest) } dependencies { @@ -65,24 +74,26 @@ dependencies { implementation(libs.plexus.utils) implementation(libs.plexus.xml) - testImplementation(libs.spock) { - exclude(group = "org.codehaus.groovy") - exclude(group = "org.hamcrest") - } testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) testImplementation(libs.xmlunit) testImplementation(libs.apache.commonsLang) testRuntimeOnly(libs.junit.platformLauncher) + funcTestImplementation(libs.spock) { + exclude(group = "org.codehaus.groovy") + exclude(group = "org.hamcrest") + } + funcTestImplementation(sourceSets.main.get().output) + lintChecks(libs.androidx.gradlePluginLints) } val integrationTest by tasks.registering(Test::class) { description = "Runs the integration tests." group = LifecycleBasePlugin.VERIFICATION_GROUP - testClassesDirs = integrationTestSourceSet.output.classesDirs - classpath = integrationTestSourceSet.runtimeClasspath + testClassesDirs = intiTest.output.classesDirs + classpath = intiTest.runtimeClasspath val docsDir = file("src/docs") // Add src/docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. @@ -90,8 +101,15 @@ val integrationTest by tasks.registering(Test::class) { systemProperty("DOCS_DIR", docsDir.absolutePath) } +val functionalTest by tasks.registering(Test::class) { + description = "Runs the functional tests." + group = LifecycleBasePlugin.VERIFICATION_GROUP + testClassesDirs = funcTest.output.classesDirs + classpath = funcTest.runtimeClasspath +} + tasks.check { - dependsOn(integrationTest) + dependsOn(integrationTest, functionalTest) } tasks.withType().configureEach { diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/ExecOutput.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/ExecOutput.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/ExecOutput.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/ExecOutput.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenDependency.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenDependency.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenDependency.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenDependency.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenMetaData.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenMetaData.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenMetaData.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenMetaData.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenScope.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenScope.groovy similarity index 100% rename from src/test/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenScope.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenScope.groovy diff --git a/src/test/resources/junit-3.8.2.jar b/src/funcTest/resources/junit-3.8.2.jar similarity index 100% rename from src/test/resources/junit-3.8.2.jar rename to src/funcTest/resources/junit-3.8.2.jar diff --git a/src/test/resources/test-artifact-1.0-SNAPSHOT.jar b/src/funcTest/resources/test-artifact-1.0-SNAPSHOT.jar similarity index 100% rename from src/test/resources/test-artifact-1.0-SNAPSHOT.jar rename to src/funcTest/resources/test-artifact-1.0-SNAPSHOT.jar diff --git a/src/test/resources/test-project-1.0-SNAPSHOT.jar b/src/funcTest/resources/test-project-1.0-SNAPSHOT.jar similarity index 100% rename from src/test/resources/test-project-1.0-SNAPSHOT.jar rename to src/funcTest/resources/test-project-1.0-SNAPSHOT.jar diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt From 8a9d317f5fce8a7b55fcbfdb922269782a43a4c5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 03:32:36 -0500 Subject: [PATCH 052/941] Merge separated ApacheNoticeResourceTransformerTest (#1063) * Remove ApacheNoticeResourceTransformerTest * Rename ApacheNoticeResourceTransformerParameterTests * Move static block * Check META-INF/MANIFEST.MF --- ...ceResourceTransformerParameterTests.groovy | 100 ------------------ ...ApacheNoticeResourceTransformerTest.groovy | 61 ++++++++++- 2 files changed, 56 insertions(+), 105 deletions(-) delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy deleted file mode 100644 index d5e0932d0..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerParameterTests.groovy +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -import static org.junit.jupiter.api.Assertions.assertTrue -import static org.junit.jupiter.api.Assertions.fail - -/** - * Tests {@link ApacheLicenseResourceTransformer} parameters. - * - * Modified from org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerParameterTests.java - */ -class ApacheNoticeResourceTransformerParameterTests extends TransformerTestSupport { - - private static final String NOTICE_RESOURCE = "META-INF/NOTICE" - private static ShadowStats stats - - @BeforeEach - void setUp() { - transformer = new ApacheNoticeResourceTransformer(objectFactory) - stats = new ShadowStats() - } - - @Test - void testCanTransformResource() { - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.TXT"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.txt"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.md"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.md"))) - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenNoInput() { - processAndFailOnNullPointer("") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenNoLinesOfInput() { - processAndFailOnNullPointer("Some notice text") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenOneLineOfInput() { - processAndFailOnNullPointer("Some notice text\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenTwoLinesOfInput() { - processAndFailOnNullPointer("Some notice text\nSome notice text\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenLineStartsWithSlashSlash() { - processAndFailOnNullPointer("Some notice text\n//Some notice text\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenLineIsSlashSlash() { - processAndFailOnNullPointer("//\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenLineIsEmpty() { - processAndFailOnNullPointer("\n") - } - - private static void processAndFailOnNullPointer(final String noticeText) { - try { - final ByteArrayInputStream noticeInputStream = new ByteArrayInputStream(noticeText.getBytes()) - final List emptyList = Collections.emptyList() - transformer.transform(TransformerContext.builder().path(NOTICE_RESOURCE).inputStream(noticeInputStream).relocators(emptyList).stats(stats).build()) - } - catch (NullPointerException ignored) { - fail("Null pointer should not be thrown when no parameters are set.") - } - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy index 4cb7ff0f7..102e35ba2 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy @@ -19,22 +19,25 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import static org.junit.jupiter.api.Assertions.assertFalse import static org.junit.jupiter.api.Assertions.assertTrue +import static org.junit.jupiter.api.Assertions.fail /** - * Test for {@link ApacheNoticeResourceTransformer}. + * Tests {@link ApacheLicenseResourceTransformer} parameters. * - * @author Benjamin Bentmann - * @version $Id: ApacheNoticeResourceTransformerTest.java 673906 2008-07-04 05:03:20Z brett $ - * - * Modified from org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerTest.java + * Modified from org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerParameterTests.java */ class ApacheNoticeResourceTransformerTest extends TransformerTestSupport { + private static final String NOTICE_RESOURCE = "META-INF/NOTICE" + private static ShadowStats stats + static { /* * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime @@ -46,6 +49,7 @@ class ApacheNoticeResourceTransformerTest extends TransformerTestSupport emptyList = Collections.emptyList() + transformer.transform(TransformerContext.builder().path(NOTICE_RESOURCE).inputStream(noticeInputStream).relocators(emptyList).stats(stats).build()) + } + catch (NullPointerException ignored) { + fail("Null pointer should not be thrown when no parameters are set.") + } + } } From 6ddf92bf3f34fd2b202d98bc635016e8664f6fe7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 04:03:23 -0500 Subject: [PATCH 053/941] Reuse Turkish locale setup (#1065) --- .../ApacheLicenseResourceTransformerTest.groovy | 6 +----- .../ApacheNoticeResourceTransformerTest.groovy | 6 +----- .../shadow/transformers/AppendingTransformerTest.groovy | 9 ++------- .../shadow/transformers/TransformerTestSupport.groovy | 8 ++++++++ .../transformers/XmlAppendingTransformerTest.groovy | 6 +----- 5 files changed, 13 insertions(+), 22 deletions(-) diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy index 85f59721d..20227b9cd 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy @@ -36,11 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue class ApacheLicenseResourceTransformerTest extends TransformerTestSupport { static { - /* - * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime - * choice to test for improper case-less string comparisions. - */ - Locale.setDefault(new Locale("tr")) + setupTurkishLocale() } @BeforeEach diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy index 102e35ba2..874e2ac3d 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy @@ -39,11 +39,7 @@ class ApacheNoticeResourceTransformerTest extends TransformerTestSupport { - static - { - /* - * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime - * choice to test for improper case-less string comparisions. - */ - Locale.setDefault(new Locale("tr")) + static { + setupTurkishLocale() } @BeforeEach diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy index a89d1ed41..a6b4e0bee 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy @@ -12,4 +12,12 @@ abstract class TransformerTestSupport { protected static FileTreeElement getFileElement(String path) { return new DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) } + + /** + * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime + * choice to test for improper case-less string comparisons. + */ + protected static setupTurkishLocale() { + Locale.setDefault(new Locale("tr")) + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy index 51cfb6c62..80dc2a2a1 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy @@ -36,11 +36,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue class XmlAppendingTransformerTest extends TransformerTestSupport { static { - /* - * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime - * choice to test for improper case-less string comparisons. - */ - Locale.setDefault(new Locale("tr")) + setupTurkishLocale() } @BeforeEach From 18f165b336c662a8e7c2abd06af1af597e18f5bf Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 04:07:03 -0500 Subject: [PATCH 054/941] Reformat unit test sources for less diffs (#1064) --- src/test/.editorconfig | 4 + .../SimpleRelocatorParameterTest.groovy | 53 +-- .../relocation/SimpleRelocatorTest.groovy | 393 ++++++++---------- ...pacheLicenseResourceTransformerTest.groovy | 61 +-- ...ApacheNoticeResourceTransformerTest.groovy | 157 +++---- .../AppendingTransformerTest.groovy | 52 +-- ...omponentsXmlResourceTransformerTest.groovy | 84 ++-- .../ManifestAppenderTransformerTest.groovy | 197 ++++----- .../PropertiesFileTransformerTest.groovy | 116 +++--- .../TransformerTestSupport.groovy | 24 +- .../XmlAppendingTransformerTest.groovy | 53 +-- 11 files changed, 505 insertions(+), 689 deletions(-) create mode 100755 src/test/.editorconfig diff --git a/src/test/.editorconfig b/src/test/.editorconfig new file mode 100755 index 000000000..a54a0eb0d --- /dev/null +++ b/src/test/.editorconfig @@ -0,0 +1,4 @@ +root = true + +[*.{groovy,java}] +indent_size = 2 diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy index 9e47404ca..28bd0a412 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy @@ -1,53 +1,30 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package com.github.jengelman.gradle.plugins.shadow.relocation import org.junit.jupiter.api.Test import static org.junit.jupiter.api.Assertions.fail - /** * Modified from org.apache.maven.plugins.shade.relocation.SimpleRelocatorParameterTest.java - * - * Modifications - * @author John Engelman */ class SimpleRelocatorParameterTest { - @Test - void testThatNullPatternInConstructorShouldNotThrowNullPointerException() { - constructThenFailOnNullPointerException(null, "") - } + @Test + void testThatNullPatternInConstructorShouldNotThrowNullPointerException() { + constructThenFailOnNullPointerException(null, "") + } - @Test - void testThatNullShadedPatternInConstructorShouldNotThrowNullPointerException() { - constructThenFailOnNullPointerException("", null) - } + @Test + void testThatNullShadedPatternInConstructorShouldNotThrowNullPointerException() { + constructThenFailOnNullPointerException("", null) + } - private static void constructThenFailOnNullPointerException(String pattern, String shadedPattern) { - try { - new SimpleRelocator(pattern, shadedPattern) - } - catch (NullPointerException ignored) { - fail("Constructor should not throw null pointer exceptions") - } + private static void constructThenFailOnNullPointerException(String pattern, String shadedPattern) { + try { + new SimpleRelocator(pattern, shadedPattern) + } + catch (NullPointerException ignored) { + fail("Constructor should not throw null pointer exceptions") } + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy index f1488c523..a68c4a418 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy @@ -1,22 +1,3 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.ShadowStats @@ -26,199 +7,191 @@ import org.junit.jupiter.api.Test import static org.junit.jupiter.api.Assertions.assertEquals /** - * Test for {@link SimpleRelocator}. - * - * @author Benjamin Bentmann - * @version $Id: SimpleRelocatorTest.java 1342979 2012-05-26 22:05:45Z bimargulies $ - * * Modified from org.apache.maven.plugins.shade.relocation.SimpleRelocatorTest.java - * - * Modifications - * @author John Engelman */ class SimpleRelocatorTest { - private static ShadowStats stats - - @BeforeEach - void setUp() { - stats = new ShadowStats() - } - - @Test - void testCanRelocatePath() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org.foo", null) - assertEquals(true, relocator.canRelocatePath("org/foo/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class.class")) - assertEquals(false, relocator.canRelocatePath("com/foo/bar/Class")) - assertEquals(false, relocator.canRelocatePath("com/foo/bar/Class.class")) - assertEquals(false, relocator.canRelocatePath("org/Foo/Class")) - assertEquals(false, relocator.canRelocatePath("org/Foo/Class.class")) - - // Verify paths starting with '/' - assertEquals(false, relocator.canRelocatePath("/org/Foo/Class")) - assertEquals(false, relocator.canRelocatePath("/org/Foo/Class.class")) - - relocator = new SimpleRelocator("org.foo", null, null, - List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) - assertEquals(true, relocator.canRelocatePath("org/foo/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/excluded")) - assertEquals(false, relocator.canRelocatePath("org/foo/Excluded")) - assertEquals(false, relocator.canRelocatePath("org/foo/Excluded.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/public")) - assertEquals(false, relocator.canRelocatePath("org/foo/public/Class")) - assertEquals(false, relocator.canRelocatePath("org/foo/public/Class.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/public/sub")) - assertEquals(true, relocator.canRelocatePath("org/foo/public/sub/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/publicRELOC/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/PrivateStuff")) - assertEquals(true, relocator.canRelocatePath("org/foo/PrivateStuff.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/PublicStuff")) - assertEquals(false, relocator.canRelocatePath("org/foo/PublicStuff.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/PublicUtilStuff")) - assertEquals(false, relocator.canRelocatePath("org/foo/PublicUtilStuff.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/Class")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/Class.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class.class")) - - // Verify edge cases - relocator = new SimpleRelocator("org.f", null) - assertEquals(false, relocator.canRelocatePath("")) // Empty path - assertEquals(false, relocator.canRelocatePath(".class")) // only .class - assertEquals(false, relocator.canRelocatePath("te")) // shorter than path pattern - assertEquals(false, relocator.canRelocatePath("test")) // shorter than path pattern with / - assertEquals(true, relocator.canRelocatePath("org/f")) // equal to path pattern - assertEquals(true, relocator.canRelocatePath("/org/f")) // equal to path pattern with / - } - - @Test - void testCanRelocatePathWithRegex() { - SimpleRelocator relocator - - // Include with Regex - relocator = new SimpleRelocator("org.foo", null, Collections.singletonList("%regex[org/foo/R(\\\$.*)?\$]"), null) - assertEquals(true, relocator.canRelocatePath("org/foo/R.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/R\$string.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/R\$layout.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/Recording/R.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/Recording.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/bar/R\$string.class")) - assertEquals(false, relocator.canRelocatePath("org/R.class")) - assertEquals(false, relocator.canRelocatePath("org/R\$string.class")) - - // Exclude with Regex - relocator = new SimpleRelocator("org.foo", null) - relocator.exclude("%regex[org/foo/.*Factory[0-9].*]") - assertEquals(true, relocator.canRelocatePath("org/foo/Factory.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/FooFactoryMain.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/BarFactory.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/Factory0.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/FooFactory1Main.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/BarFactory2.class")) - - // Include with Regex and normal pattern - relocator = new SimpleRelocator("org.foo", null, - List.of("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*")) - assertEquals(true, relocator.canRelocatePath("org/foo/Factory1.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/public/Bar.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/Factory.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/R.class")) - } - - @Test - void testCanRelocateClass() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org.foo", null) - assertEquals(true, relocator.canRelocateClass("org.foo.Class")) - assertEquals(true, relocator.canRelocateClass("org.foo.bar.Class")) - assertEquals(false, relocator.canRelocateClass("com.foo.bar.Class")) - assertEquals(false, relocator.canRelocateClass("org.Foo.Class")) - - relocator = new SimpleRelocator("org.foo", null, null, - List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) - assertEquals(true, relocator.canRelocateClass("org.foo.Class")) - assertEquals(true, relocator.canRelocateClass("org.foo.excluded")) - assertEquals(false, relocator.canRelocateClass("org.foo.Excluded")) - assertEquals(false, relocator.canRelocateClass("org.foo.public")) - assertEquals(false, relocator.canRelocateClass("org.foo.public.Class")) - assertEquals(false, relocator.canRelocateClass("org.foo.public.sub")) - assertEquals(true, relocator.canRelocateClass("org.foo.public.sub.Class")) - assertEquals(true, relocator.canRelocateClass("org.foo.publicRELOC.Class")) - assertEquals(true, relocator.canRelocateClass("org.foo.PrivateStuff")) - assertEquals(false, relocator.canRelocateClass("org.foo.PublicStuff")) - assertEquals(false, relocator.canRelocateClass("org.foo.PublicUtilStuff")) - assertEquals(false, relocator.canRelocateClass("org.foo.recurse")) - assertEquals(false, relocator.canRelocateClass("org.foo.recurse.Class")) - assertEquals(false, relocator.canRelocateClass("org.foo.recurse.sub")) - assertEquals(false, relocator.canRelocateClass("org.foo.recurse.sub.Class")) - } - - @Test - void testCanRelocateRawString() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org/foo", null, null, null, true) - assertEquals(true, relocator.canRelocatePath("(I)org/foo/bar/Class")) - - relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", null, null, null, true) - assertEquals(true, relocator.canRelocatePath("META-INF/org.foo.xml")) - } - - //MSHADE-119, make sure that the easy part of this works. - @Test - void testCanRelocateAbsClassPath() { - SimpleRelocator relocator = new SimpleRelocator("org.apache.velocity", "org.apache.momentum") - assertEquals("/org/apache/momentum/mass.properties", relocator.relocatePath(pathContext("/org/apache/velocity/mass.properties"))) - - } - - @Test - void testRelocatePath() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org.foo", null) - assertEquals("hidden/org/foo/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) - - relocator = new SimpleRelocator("org.foo", "private.stuff") - assertEquals("private/stuff/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) - } - - @Test - void testRelocateClass() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org.foo", null) - assertEquals("hidden.org.foo.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) - - relocator = new SimpleRelocator("org.foo", "private.stuff") - assertEquals("private.stuff.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) - } - - @Test - void testRelocateRawString() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("Lorg/foo", "Lhidden/org/foo", null, null, true) - assertEquals("(I)Lhidden/org/foo/bar/Class", relocator.relocatePath(pathContext("(I)Lorg/foo/bar/Class"))) - - relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", null, null, true) - assertEquals("META-INF/hidden.org.foo.xml", relocator.relocatePath(pathContext("META-INF/org.foo.xml"))) - } - - protected static RelocatePathContext pathContext(String path) { - return RelocatePathContext.builder().path(path).stats(stats).build() - } - - protected static RelocateClassContext classContext(String className) { - return RelocateClassContext.builder().className(className).stats(stats).build() - } + private static ShadowStats stats + + @BeforeEach + void setUp() { + stats = new ShadowStats() + } + + @Test + void testCanRelocatePath() { + SimpleRelocator relocator + + relocator = new SimpleRelocator("org.foo", null) + assertEquals(true, relocator.canRelocatePath("org/foo/Class")) + assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) + assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class")) + assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class.class")) + assertEquals(false, relocator.canRelocatePath("com/foo/bar/Class")) + assertEquals(false, relocator.canRelocatePath("com/foo/bar/Class.class")) + assertEquals(false, relocator.canRelocatePath("org/Foo/Class")) + assertEquals(false, relocator.canRelocatePath("org/Foo/Class.class")) + + // Verify paths starting with '/' + assertEquals(false, relocator.canRelocatePath("/org/Foo/Class")) + assertEquals(false, relocator.canRelocatePath("/org/Foo/Class.class")) + + relocator = new SimpleRelocator("org.foo", null, null, + List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) + assertEquals(true, relocator.canRelocatePath("org/foo/Class")) + assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) + assertEquals(true, relocator.canRelocatePath("org/foo/excluded")) + assertEquals(false, relocator.canRelocatePath("org/foo/Excluded")) + assertEquals(false, relocator.canRelocatePath("org/foo/Excluded.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/public")) + assertEquals(false, relocator.canRelocatePath("org/foo/public/Class")) + assertEquals(false, relocator.canRelocatePath("org/foo/public/Class.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/public/sub")) + assertEquals(true, relocator.canRelocatePath("org/foo/public/sub/Class")) + assertEquals(true, relocator.canRelocatePath("org/foo/publicRELOC/Class")) + assertEquals(true, relocator.canRelocatePath("org/foo/PrivateStuff")) + assertEquals(true, relocator.canRelocatePath("org/foo/PrivateStuff.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/PublicStuff")) + assertEquals(false, relocator.canRelocatePath("org/foo/PublicStuff.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/PublicUtilStuff")) + assertEquals(false, relocator.canRelocatePath("org/foo/PublicUtilStuff.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/recurse")) + assertEquals(false, relocator.canRelocatePath("org/foo/recurse/Class")) + assertEquals(false, relocator.canRelocatePath("org/foo/recurse/Class.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub")) + assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class")) + assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class.class")) + + // Verify edge cases + relocator = new SimpleRelocator("org.f", null) + assertEquals(false, relocator.canRelocatePath("")) // Empty path + assertEquals(false, relocator.canRelocatePath(".class")) // only .class + assertEquals(false, relocator.canRelocatePath("te")) // shorter than path pattern + assertEquals(false, relocator.canRelocatePath("test")) // shorter than path pattern with / + assertEquals(true, relocator.canRelocatePath("org/f")) // equal to path pattern + assertEquals(true, relocator.canRelocatePath("/org/f")) // equal to path pattern with / + } + + @Test + void testCanRelocatePathWithRegex() { + SimpleRelocator relocator + + // Include with Regex + relocator = new SimpleRelocator("org.foo", null, Collections.singletonList("%regex[org/foo/R(\\\$.*)?\$]"), null) + assertEquals(true, relocator.canRelocatePath("org/foo/R.class")) + assertEquals(true, relocator.canRelocatePath("org/foo/R\$string.class")) + assertEquals(true, relocator.canRelocatePath("org/foo/R\$layout.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/Recording/R.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/Recording.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/bar/R\$string.class")) + assertEquals(false, relocator.canRelocatePath("org/R.class")) + assertEquals(false, relocator.canRelocatePath("org/R\$string.class")) + + // Exclude with Regex + relocator = new SimpleRelocator("org.foo", null) + relocator.exclude("%regex[org/foo/.*Factory[0-9].*]") + assertEquals(true, relocator.canRelocatePath("org/foo/Factory.class")) + assertEquals(true, relocator.canRelocatePath("org/foo/FooFactoryMain.class")) + assertEquals(true, relocator.canRelocatePath("org/foo/BarFactory.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/Factory0.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/FooFactory1Main.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/BarFactory2.class")) + + // Include with Regex and normal pattern + relocator = new SimpleRelocator("org.foo", null, + List.of("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*")) + assertEquals(true, relocator.canRelocatePath("org/foo/Factory1.class")) + assertEquals(true, relocator.canRelocatePath("org/foo/public/Bar.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/Factory.class")) + assertEquals(false, relocator.canRelocatePath("org/foo/R.class")) + } + + @Test + void testCanRelocateClass() { + SimpleRelocator relocator + + relocator = new SimpleRelocator("org.foo", null) + assertEquals(true, relocator.canRelocateClass("org.foo.Class")) + assertEquals(true, relocator.canRelocateClass("org.foo.bar.Class")) + assertEquals(false, relocator.canRelocateClass("com.foo.bar.Class")) + assertEquals(false, relocator.canRelocateClass("org.Foo.Class")) + + relocator = new SimpleRelocator("org.foo", null, null, + List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) + assertEquals(true, relocator.canRelocateClass("org.foo.Class")) + assertEquals(true, relocator.canRelocateClass("org.foo.excluded")) + assertEquals(false, relocator.canRelocateClass("org.foo.Excluded")) + assertEquals(false, relocator.canRelocateClass("org.foo.public")) + assertEquals(false, relocator.canRelocateClass("org.foo.public.Class")) + assertEquals(false, relocator.canRelocateClass("org.foo.public.sub")) + assertEquals(true, relocator.canRelocateClass("org.foo.public.sub.Class")) + assertEquals(true, relocator.canRelocateClass("org.foo.publicRELOC.Class")) + assertEquals(true, relocator.canRelocateClass("org.foo.PrivateStuff")) + assertEquals(false, relocator.canRelocateClass("org.foo.PublicStuff")) + assertEquals(false, relocator.canRelocateClass("org.foo.PublicUtilStuff")) + assertEquals(false, relocator.canRelocateClass("org.foo.recurse")) + assertEquals(false, relocator.canRelocateClass("org.foo.recurse.Class")) + assertEquals(false, relocator.canRelocateClass("org.foo.recurse.sub")) + assertEquals(false, relocator.canRelocateClass("org.foo.recurse.sub.Class")) + } + + @Test + void testCanRelocateRawString() { + SimpleRelocator relocator + + relocator = new SimpleRelocator("org/foo", null, null, null, true) + assertEquals(true, relocator.canRelocatePath("(I)org/foo/bar/Class")) + + relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", null, null, null, true) + assertEquals(true, relocator.canRelocatePath("META-INF/org.foo.xml")) + } + + //MSHADE-119, make sure that the easy part of this works. + @Test + void testCanRelocateAbsClassPath() { + SimpleRelocator relocator = new SimpleRelocator("org.apache.velocity", "org.apache.momentum") + assertEquals("/org/apache/momentum/mass.properties", relocator.relocatePath(pathContext("/org/apache/velocity/mass.properties"))) + + } + + @Test + void testRelocatePath() { + SimpleRelocator relocator + + relocator = new SimpleRelocator("org.foo", null) + assertEquals("hidden/org/foo/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) + + relocator = new SimpleRelocator("org.foo", "private.stuff") + assertEquals("private/stuff/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) + } + + @Test + void testRelocateClass() { + SimpleRelocator relocator + + relocator = new SimpleRelocator("org.foo", null) + assertEquals("hidden.org.foo.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) + + relocator = new SimpleRelocator("org.foo", "private.stuff") + assertEquals("private.stuff.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) + } + + @Test + void testRelocateRawString() { + SimpleRelocator relocator + + relocator = new SimpleRelocator("Lorg/foo", "Lhidden/org/foo", null, null, true) + assertEquals("(I)Lhidden/org/foo/bar/Class", relocator.relocatePath(pathContext("(I)Lorg/foo/bar/Class"))) + + relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", null, null, true) + assertEquals("META-INF/hidden.org.foo.xml", relocator.relocatePath(pathContext("META-INF/org.foo.xml"))) + } + + protected static RelocatePathContext pathContext(String path) { + return RelocatePathContext.builder().path(path).stats(stats).build() + } + + protected static RelocateClassContext classContext(String className) { + return RelocateClassContext.builder().className(className).stats(stats).build() + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy index 20227b9cd..27117c51c 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy @@ -1,22 +1,3 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package com.github.jengelman.gradle.plugins.shadow.transformers import org.junit.jupiter.api.BeforeEach @@ -26,32 +7,26 @@ import static org.junit.jupiter.api.Assertions.assertFalse import static org.junit.jupiter.api.Assertions.assertTrue /** - * Test for {@link ApacheLicenseResourceTransformer}. - * - * @author Benjamin Bentmann - * @version $Id: ApacheLicenseResourceTransformerTest.java 673906 2008-07-04 05:03:20Z brett $ - * * Modified from org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformerTest.java */ class ApacheLicenseResourceTransformerTest extends TransformerTestSupport { - static { - setupTurkishLocale() - } - - @BeforeEach - void setUp() { - transformer = new ApacheLicenseResourceTransformer() - } - - @Test - void testCanTransformResource() { - assertTrue(transformer.canTransformResource(getFileElement("META-INF/LICENSE"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/LICENSE.TXT"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/License.txt"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/LICENSE.md"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/License.md"))) - assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) - } - + static { + setupTurkishLocale() + } + + @BeforeEach + void setUp() { + transformer = new ApacheLicenseResourceTransformer() + } + + @Test + void testCanTransformResource() { + assertTrue(transformer.canTransformResource(getFileElement("META-INF/LICENSE"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/LICENSE.TXT"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/License.txt"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/LICENSE.md"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/License.md"))) + assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy index 874e2ac3d..0490305e1 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy @@ -1,22 +1,3 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.ShadowStats @@ -24,83 +5,79 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import static org.junit.jupiter.api.Assertions.assertFalse -import static org.junit.jupiter.api.Assertions.assertTrue -import static org.junit.jupiter.api.Assertions.fail +import static org.junit.jupiter.api.Assertions.* /** - * Tests {@link ApacheLicenseResourceTransformer} parameters. - * * Modified from org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerParameterTests.java */ class ApacheNoticeResourceTransformerTest extends TransformerTestSupport { - private static final String NOTICE_RESOURCE = "META-INF/NOTICE" - private static ShadowStats stats - - static { - setupTurkishLocale() - } - - @BeforeEach - void setUp() { - transformer = new ApacheNoticeResourceTransformer(objectFactory) - stats = new ShadowStats() - } - - @Test - void testCanTransformResource() { - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.TXT"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.txt"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.md"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.md"))) - assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenNoInput() { - processAndFailOnNullPointer("") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenNoLinesOfInput() { - processAndFailOnNullPointer("Some notice text") + private static final String NOTICE_RESOURCE = "META-INF/NOTICE" + private static ShadowStats stats + + static { + setupTurkishLocale() + } + + @BeforeEach + void setUp() { + transformer = new ApacheNoticeResourceTransformer(objectFactory) + stats = new ShadowStats() + } + + @Test + void testCanTransformResource() { + assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.TXT"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.txt"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.md"))) + assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.md"))) + assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) + } + + @Test + void testNoParametersShouldNotThrowNullPointerWhenNoInput() { + processAndFailOnNullPointer("") + } + + @Test + void testNoParametersShouldNotThrowNullPointerWhenNoLinesOfInput() { + processAndFailOnNullPointer("Some notice text") + } + + @Test + void testNoParametersShouldNotThrowNullPointerWhenOneLineOfInput() { + processAndFailOnNullPointer("Some notice text\n") + } + + @Test + void testNoParametersShouldNotThrowNullPointerWhenTwoLinesOfInput() { + processAndFailOnNullPointer("Some notice text\nSome notice text\n") + } + + @Test + void testNoParametersShouldNotThrowNullPointerWhenLineStartsWithSlashSlash() { + processAndFailOnNullPointer("Some notice text\n//Some notice text\n") + } + + @Test + void testNoParametersShouldNotThrowNullPointerWhenLineIsSlashSlash() { + processAndFailOnNullPointer("//\n") + } + + @Test + void testNoParametersShouldNotThrowNullPointerWhenLineIsEmpty() { + processAndFailOnNullPointer("\n") + } + + private static void processAndFailOnNullPointer(final String noticeText) { + try { + final ByteArrayInputStream noticeInputStream = new ByteArrayInputStream(noticeText.getBytes()) + final List emptyList = Collections.emptyList() + transformer.transform(TransformerContext.builder().path(NOTICE_RESOURCE).inputStream(noticeInputStream).relocators(emptyList).stats(stats).build()) } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenOneLineOfInput() { - processAndFailOnNullPointer("Some notice text\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenTwoLinesOfInput() { - processAndFailOnNullPointer("Some notice text\nSome notice text\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenLineStartsWithSlashSlash() { - processAndFailOnNullPointer("Some notice text\n//Some notice text\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenLineIsSlashSlash() { - processAndFailOnNullPointer("//\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenLineIsEmpty() { - processAndFailOnNullPointer("\n") - } - - private static void processAndFailOnNullPointer(final String noticeText) { - try { - final ByteArrayInputStream noticeInputStream = new ByteArrayInputStream(noticeText.getBytes()) - final List emptyList = Collections.emptyList() - transformer.transform(TransformerContext.builder().path(NOTICE_RESOURCE).inputStream(noticeInputStream).relocators(emptyList).stats(stats).build()) - } - catch (NullPointerException ignored) { - fail("Null pointer should not be thrown when no parameters are set.") - } + catch (NullPointerException ignored) { + fail("Null pointer should not be thrown when no parameters are set.") } + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy index 68a7950b3..f9d4b8f59 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy @@ -1,22 +1,3 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package com.github.jengelman.gradle.plugins.shadow.transformers import org.junit.jupiter.api.BeforeEach @@ -26,29 +7,26 @@ import static org.junit.jupiter.api.Assertions.assertFalse import static org.junit.jupiter.api.Assertions.assertTrue /** - * Test for {@link AppendingTransformer}. - * - * @author Benjamin Bentmann - * @version $Id: AppendingTransformerTest.java 673906 2008-07-04 05:03:20Z brett $ + * Modified from org.apache.maven.plugins.shade.resource.AppendingTransformerTest.java */ class AppendingTransformerTest extends TransformerTestSupport { - static { - setupTurkishLocale() - } + static { + setupTurkishLocale() + } - @BeforeEach - void setUp() { - transformer = new AppendingTransformer(objectFactory) - } + @BeforeEach + void setUp() { + transformer = new AppendingTransformer(objectFactory) + } - @Test - void testCanTransformResource() { - transformer.resource.set("abcdefghijklmnopqrstuvwxyz") + @Test + void testCanTransformResource() { + transformer.resource.set("abcdefghijklmnopqrstuvwxyz") - assertTrue(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))) - assertTrue(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) - assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) - } + assertTrue(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))) + assertTrue(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) + assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy index edf9ea9e1..537feba60 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy @@ -1,22 +1,3 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.ShadowStats @@ -29,46 +10,41 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test /** - * Test for {@link ComponentsXmlResourceTransformer}. - * - * @author Brett Porter - * @version $Id: ComponentsXmlResourceTransformerTest.java 1379994 2012-09-02 15:22:49Z hboutemy $ - * * Modified from org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformerTest.java */ class ComponentsXmlResourceTransformerTest extends TransformerTestSupport { - private static ShadowStats stats + private static ShadowStats stats - @BeforeEach - void setUp() { - transformer = new ComponentsXmlResourceTransformer() - stats = new ShadowStats() - } + @BeforeEach + void setUp() { + transformer = new ComponentsXmlResourceTransformer() + stats = new ShadowStats() + } - @Test - void testConfigurationMerging() { + @Test + void testConfigurationMerging() { - XMLUnit.setNormalizeWhitespace(true) + XMLUnit.setNormalizeWhitespace(true) - transformer.transform( - TransformerContext.builder() - .path("components-1.xml") - .inputStream(getClass().getResourceAsStream("/components-1.xml")) - .relocators(Collections. emptyList()) - .stats(stats) - .build()) - transformer.transform( - TransformerContext.builder() - .path("components-1.xml") - .inputStream(getClass().getResourceAsStream("/components-2.xml")) - .relocators(Collections. emptyList()) - .stats(stats) - .build()) - Diff diff = XMLUnit.compareXML( - IOUtil.toString(getClass().getResourceAsStream("/components-expected.xml"), "UTF-8"), - IOUtil.toString(transformer.getTransformedResource(), "UTF-8")) - //assertEquals( IOUtil.toString( getClass().getResourceAsStream( "/components-expected.xml" ), "UTF-8" ), - // IOUtil.toString( transformer.getTransformedResource(), "UTF-8" ).replaceAll("\r\n", "\n") ) - XMLAssert.assertXMLIdentical(diff, true) - } + transformer.transform( + TransformerContext.builder() + .path("components-1.xml") + .inputStream(getClass().getResourceAsStream("/components-1.xml")) + .relocators(Collections. emptyList()) + .stats(stats) + .build()) + transformer.transform( + TransformerContext.builder() + .path("components-1.xml") + .inputStream(getClass().getResourceAsStream("/components-2.xml")) + .relocators(Collections. emptyList()) + .stats(stats) + .build()) + Diff diff = XMLUnit.compareXML( + IOUtil.toString(getClass().getResourceAsStream("/components-expected.xml"), "UTF-8"), + IOUtil.toString(transformer.getTransformedResource(), "UTF-8")) + //assertEquals( IOUtil.toString( getClass().getResourceAsStream( "/components-expected.xml" ), "UTF-8" ), + // IOUtil.toString( transformer.getTransformedResource(), "UTF-8" ).replaceAll("\r\n", "\n") ) + XMLAssert.assertXMLIdentical(diff, true) + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy index 46e325a6a..48719fce6 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy @@ -1,22 +1,3 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.ShadowStats @@ -34,107 +15,107 @@ import static org.junit.jupiter.api.Assertions.* * Test for {@link ManifestAppenderTransformer}. */ class ManifestAppenderTransformerTest extends TransformerTestSupport { - private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF" - - @BeforeEach - void setUp() { - transformer = new ManifestAppenderTransformer(objectFactory) + private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF" + + @BeforeEach + void setUp() { + transformer = new ManifestAppenderTransformer(objectFactory) + } + + @Test + void testCanTransformResource() { + transformer.with { + append('Name', 'org/foo/bar/') + append('Sealed', true) } - @Test - void testCanTransformResource() { - transformer.with { - append('Name', 'org/foo/bar/') - append('Sealed', true) - } + assertTrue(transformer.canTransformResource(getFileElement(MANIFEST_NAME))) + assertTrue(transformer.canTransformResource(getFileElement(MANIFEST_NAME.toLowerCase()))) + } - assertTrue(transformer.canTransformResource(getFileElement(MANIFEST_NAME))) - assertTrue(transformer.canTransformResource(getFileElement(MANIFEST_NAME.toLowerCase()))) - } + @Test + void testHasTransformedResource() { + transformer.append('Tag', 'Something') - @Test - void testHasTransformedResource() { - transformer.append('Tag', 'Something') + assertTrue(transformer.hasTransformedResource()) + } - assertTrue(transformer.hasTransformedResource()) - } + @Test + void testHasNotTransformedResource() { + assertFalse(transformer.hasTransformedResource()) + } - @Test - void testHasNotTransformedResource() { - assertFalse(transformer.hasTransformedResource()) - } + @Test + void testTransformation() { + transformer.with { + append('Name', 'org/foo/bar/') + append('Sealed', true) + append('Name', 'com/example/') + append('Sealed', false) - @Test - void testTransformation() { - transformer.with { - append('Name', 'org/foo/bar/') - append('Sealed', true) - append('Name', 'com/example/') - append('Sealed', false) - - transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) - } - - def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") - def fileOutputStream = new FileOutputStream(testableZipFile) - def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) - def zipOutputStream = new ZipOutputStream(bufferedOutputStream) - - try { - transformer.modifyOutputStream(zipOutputStream, true) - } finally { - zipOutputStream.close() - } - - def targetLines = readFrom(testableZipFile, MANIFEST_NAME) - assertFalse(targetLines.isEmpty()) - assertTrue(targetLines.size() > 4) - - def trailer = targetLines.with { subList(size() - 5, size()) } - assertEquals(asList( - "Name: org/foo/bar/", - "Sealed: true", - "Name: com/example/", - "Sealed: false", - ""), trailer - ) + transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) } - @Test - void testNoTransformation() { - def sourceLines = getResourceStream(MANIFEST_NAME).readLines() + def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") + def fileOutputStream = new FileOutputStream(testableZipFile) + def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) + def zipOutputStream = new ZipOutputStream(bufferedOutputStream) - transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) - - def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") - def fileOutputStream = new FileOutputStream(testableZipFile) - def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) - def zipOutputStream = new ZipOutputStream(bufferedOutputStream) - - try { - transformer.modifyOutputStream(zipOutputStream, true) - } finally { - zipOutputStream.close() - } - def targetLines = readFrom(testableZipFile, MANIFEST_NAME) - - assertEquals(sourceLines, targetLines) + try { + transformer.modifyOutputStream(zipOutputStream, true) + } finally { + zipOutputStream.close() } - static List readFrom(File jarFile, String resourceName) { - def zip = new ZipFile(jarFile) - try { - def entry = zip.getEntry(resourceName) - if (!entry) { - return Collections.emptyList() - } - return zip.getInputStream(entry).readLines() - } finally { - zip.close() - } + def targetLines = readFrom(testableZipFile, MANIFEST_NAME) + assertFalse(targetLines.isEmpty()) + assertTrue(targetLines.size() > 4) + + def trailer = targetLines.with { subList(size() - 5, size()) } + assertEquals(asList( + "Name: org/foo/bar/", + "Sealed: true", + "Name: com/example/", + "Sealed: false", + ""), trailer + ) + } + + @Test + void testNoTransformation() { + def sourceLines = getResourceStream(MANIFEST_NAME).readLines() + + transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) + + def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") + def fileOutputStream = new FileOutputStream(testableZipFile) + def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) + def zipOutputStream = new ZipOutputStream(bufferedOutputStream) + + try { + transformer.modifyOutputStream(zipOutputStream, true) + } finally { + zipOutputStream.close() } - - InputStream getResourceStream(String resource) { - this.class.classLoader.getResourceAsStream(resource) + def targetLines = readFrom(testableZipFile, MANIFEST_NAME) + + assertEquals(sourceLines, targetLines) + } + + static List readFrom(File jarFile, String resourceName) { + def zip = new ZipFile(jarFile) + try { + def entry = zip.getEntry(resourceName) + if (!entry) { + return Collections.emptyList() + } + return zip.getInputStream(entry).readLines() + } finally { + zip.close() } + } + + InputStream getResourceStream(String resource) { + this.class.classLoader.getResourceAsStream(resource) + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy index 46233585d..0e3e3c263 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy @@ -14,81 +14,81 @@ import static org.junit.jupiter.api.Assertions.* * Test for {@link PropertiesFileTransformer}. */ final class PropertiesFileTransformerTest extends TransformerTestSupport { - private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF" + private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF" - @BeforeEach - void setUp() { - transformer = new PropertiesFileTransformer(objectFactory) - } + @BeforeEach + void setUp() { + transformer = new PropertiesFileTransformer(objectFactory) + } - @Test - void testHasTransformedResource() { - transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME))) + @Test + void testHasTransformedResource() { + transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME))) - assertTrue(transformer.hasTransformedResource()) - } + assertTrue(transformer.hasTransformedResource()) + } - @Test - void testHasNotTransformedResource() { - assertFalse(transformer.hasTransformedResource()) - } + @Test + void testHasNotTransformedResource() { + assertFalse(transformer.hasTransformedResource()) + } - @Test - void testTransformation() { - transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) + @Test + void testTransformation() { + transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) - def testableZipFile = doTransformAndGetTransformedFile(transformer, false) - def targetLines = readFrom(testableZipFile, MANIFEST_NAME) + def testableZipFile = doTransformAndGetTransformedFile(transformer, false) + def targetLines = readFrom(testableZipFile, MANIFEST_NAME) - assertFalse(targetLines.isEmpty()) + assertFalse(targetLines.isEmpty()) - assertTrue(targetLines.contains("Manifest-Version=1.0")) - } + assertTrue(targetLines.contains("Manifest-Version=1.0")) + } - @Test - void testTransformationPropertiesAreReproducible() { - transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) + @Test + void testTransformationPropertiesAreReproducible() { + transformer.transform(new TransformerContext(MANIFEST_NAME, getResourceStream(MANIFEST_NAME), Collections. emptyList(), new ShadowStats())) - def firstRunTransformedFile = doTransformAndGetTransformedFile(transformer, true) - def firstRunTargetLines = readFrom(firstRunTransformedFile, MANIFEST_NAME) + def firstRunTransformedFile = doTransformAndGetTransformedFile(transformer, true) + def firstRunTargetLines = readFrom(firstRunTransformedFile, MANIFEST_NAME) - Thread.sleep(1000) // wait for 1sec to ensure timestamps in properties would change + Thread.sleep(1000) // wait for 1sec to ensure timestamps in properties would change - def secondRunTransformedFile = doTransformAndGetTransformedFile(transformer, true) - def secondRunTargetLines = readFrom(secondRunTransformedFile, MANIFEST_NAME) + def secondRunTransformedFile = doTransformAndGetTransformedFile(transformer, true) + def secondRunTargetLines = readFrom(secondRunTransformedFile, MANIFEST_NAME) - assertEquals(firstRunTargetLines, secondRunTargetLines) - } - - static File doTransformAndGetTransformedFile(final PropertiesFileTransformer transformer, final boolean preserveFileTimestamps) { - def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") - def fileOutputStream = new FileOutputStream(testableZipFile) - def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) - def zipOutputStream = new ZipOutputStream(bufferedOutputStream) + assertEquals(firstRunTargetLines, secondRunTargetLines) + } - try { - transformer.modifyOutputStream(zipOutputStream, preserveFileTimestamps) - } finally { - zipOutputStream.close() - } + static File doTransformAndGetTransformedFile(final PropertiesFileTransformer transformer, final boolean preserveFileTimestamps) { + def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") + def fileOutputStream = new FileOutputStream(testableZipFile) + def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) + def zipOutputStream = new ZipOutputStream(bufferedOutputStream) - return testableZipFile + try { + transformer.modifyOutputStream(zipOutputStream, preserveFileTimestamps) + } finally { + zipOutputStream.close() } - static List readFrom(File jarFile, String resourceName) { - def zip = new ZipFile(jarFile) - try { - def entry = zip.getEntry(resourceName) - if (!entry) { - return Collections.emptyList() - } - return zip.getInputStream(entry).readLines() - } finally { - zip.close() - } + return testableZipFile + } + + static List readFrom(File jarFile, String resourceName) { + def zip = new ZipFile(jarFile) + try { + def entry = zip.getEntry(resourceName) + if (!entry) { + return Collections.emptyList() + } + return zip.getInputStream(entry).readLines() + } finally { + zip.close() } + } - InputStream getResourceStream(String resource) { - this.class.classLoader.getResourceAsStream(resource) - } + InputStream getResourceStream(String resource) { + this.class.classLoader.getResourceAsStream(resource) + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy index a6b4e0bee..4cca70207 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy @@ -6,18 +6,18 @@ import org.gradle.api.internal.file.DefaultFileTreeElement import org.gradle.testfixtures.ProjectBuilder abstract class TransformerTestSupport { - protected static T transformer - protected static final def objectFactory = ProjectBuilder.builder().build().objects + protected static T transformer + protected static final def objectFactory = ProjectBuilder.builder().build().objects - protected static FileTreeElement getFileElement(String path) { - return new DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) - } + protected static FileTreeElement getFileElement(String path) { + return new DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) + } - /** - * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime - * choice to test for improper case-less string comparisons. - */ - protected static setupTurkishLocale() { - Locale.setDefault(new Locale("tr")) - } + /** + * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime + * choice to test for improper case-less string comparisons. + */ + protected static setupTurkishLocale() { + Locale.setDefault(new Locale("tr")) + } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy index 80dc2a2a1..71c02736c 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy @@ -1,22 +1,3 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package com.github.jengelman.gradle.plugins.shadow.transformers import org.junit.jupiter.api.BeforeEach @@ -26,31 +7,25 @@ import static org.junit.jupiter.api.Assertions.assertFalse import static org.junit.jupiter.api.Assertions.assertTrue /** - * Test for {@link XmlAppendingTransformer}. - * - * @author Benjamin Bentmann - * @version $Id: XmlAppendingTransformerTest.java 673906 2008-07-04 05:03:20Z brett $ - * * Modified from org.apache.maven.plugins.shade.resource.XmlAppendingTransformerTest.java */ class XmlAppendingTransformerTest extends TransformerTestSupport { - static { - setupTurkishLocale() - } - - @BeforeEach - void setUp() { - transformer = new XmlAppendingTransformer(objectFactory) - } + static { + setupTurkishLocale() + } - @Test - void testCanTransformResource() { - transformer.resource.set("abcdefghijklmnopqrstuvwxyz") + @BeforeEach + void setUp() { + transformer = new XmlAppendingTransformer(objectFactory) + } - assertTrue(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))) - assertTrue(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) - assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) - } + @Test + void testCanTransformResource() { + transformer.resource.set("abcdefghijklmnopqrstuvwxyz") + assertTrue(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))) + assertTrue(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) + assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) + } } From 98b3fa07171ce40407c5f01c7011306977a4558d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 07:40:41 -0500 Subject: [PATCH 055/941] Overload SimpleRelocator (#1067) Keep the binary compatibility with 8.x versions. --- api/shadow.api | 2 ++ .../gradle/plugins/shadow/relocation/SimpleRelocator.kt | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 7df232de7..c3c85ab58 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -181,6 +181,8 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat } public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator : com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { + public fun ()V + public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;Ljava/lang/String;)V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 15311b17d..7ec2b8c3e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -14,8 +14,8 @@ import org.gradle.api.tasks.Optional */ @CacheableRelocator public open class SimpleRelocator @JvmOverloads constructor( - pattern: String?, - shadedPattern: String?, + pattern: String? = null, + shadedPattern: String? = null, includes: List? = null, excludes: List? = null, private val _rawString: Boolean = false, From 52e0f20f0b0b8dacb4b4e3b03f8902e4d3caaa59 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 07:56:11 -0500 Subject: [PATCH 056/941] Simplify new instances in tests (#1066) --- ...g4j2PluginsCacheFileTransformerSpec.groovy | 7 +++--- .../TransformerSpecSupport.groovy | 4 +-- .../relocation/SimpleRelocatorTest.groovy | 23 ++++++----------- ...ApacheNoticeResourceTransformerTest.groovy | 9 ++++--- ...omponentsXmlResourceTransformerTest.groovy | 25 ++++++------------- .../ManifestAppenderTransformerTest.groovy | 11 +++----- .../PropertiesFileTransformerTest.groovy | 13 +++------- .../TransformerTestSupport.groovy | 5 ++++ 8 files changed, 37 insertions(+), 60 deletions(-) diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy index e48aaf66a..abacd4498 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy @@ -34,8 +34,7 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { void "should transform"() { given: - List relocators = new ArrayList<>() - relocators.add(new SimpleRelocator(null, null)) + def relocators = singletonList(new SimpleRelocator()) when: transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators)) @@ -49,10 +48,10 @@ class Log4j2PluginsCacheFileTransformerSpec extends Specification { String pattern = "org.apache.logging" String destination = "new.location.org.apache.logging" - List relocators = singletonList((Relocator) new SimpleRelocator(pattern, destination)) + List relocators = singletonList(new SimpleRelocator(pattern, destination)) when: - transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators, new ShadowStats())) + transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators)) then: transformer.hasTransformedResource() diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy index 88b327a42..e6384cb78 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy @@ -50,11 +50,11 @@ class TransformerSpecSupport extends Specification { } protected TransformerContext context(String path, Map input, String charset = 'ISO_8859_1') { - TransformerContext.builder().path(path).inputStream(toInputStream(toProperties(input), charset)).relocators([]).stats(stats).build() + TransformerContext.builder().path(path).inputStream(toInputStream(toProperties(input), charset)).stats(stats).build() } protected TransformerContext context(String path, String input) { - TransformerContext.builder().path(path).inputStream(toInputStream(input)).relocators([]).stats(stats).build() + TransformerContext.builder().path(path).inputStream(toInputStream(input)).stats(stats).build() } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy index a68c4a418..1373c9ed5 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy @@ -11,18 +11,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals */ class SimpleRelocatorTest { - private static ShadowStats stats - - @BeforeEach - void setUp() { - stats = new ShadowStats() - } - @Test void testCanRelocatePath() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator("org.foo") assertEquals(true, relocator.canRelocatePath("org/foo/Class")) assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class")) @@ -63,7 +56,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class.class")) // Verify edge cases - relocator = new SimpleRelocator("org.f", null) + relocator = new SimpleRelocator("org.f") assertEquals(false, relocator.canRelocatePath("")) // Empty path assertEquals(false, relocator.canRelocatePath(".class")) // only .class assertEquals(false, relocator.canRelocatePath("te")) // shorter than path pattern @@ -88,7 +81,7 @@ class SimpleRelocatorTest { assertEquals(false, relocator.canRelocatePath("org/R\$string.class")) // Exclude with Regex - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator("org.foo") relocator.exclude("%regex[org/foo/.*Factory[0-9].*]") assertEquals(true, relocator.canRelocatePath("org/foo/Factory.class")) assertEquals(true, relocator.canRelocatePath("org/foo/FooFactoryMain.class")) @@ -110,7 +103,7 @@ class SimpleRelocatorTest { void testCanRelocateClass() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator("org.foo") assertEquals(true, relocator.canRelocateClass("org.foo.Class")) assertEquals(true, relocator.canRelocateClass("org.foo.bar.Class")) assertEquals(false, relocator.canRelocateClass("com.foo.bar.Class")) @@ -158,7 +151,7 @@ class SimpleRelocatorTest { void testRelocatePath() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator("org.foo") assertEquals("hidden/org/foo/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) relocator = new SimpleRelocator("org.foo", "private.stuff") @@ -169,7 +162,7 @@ class SimpleRelocatorTest { void testRelocateClass() { SimpleRelocator relocator - relocator = new SimpleRelocator("org.foo", null) + relocator = new SimpleRelocator("org.foo") assertEquals("hidden.org.foo.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) relocator = new SimpleRelocator("org.foo", "private.stuff") @@ -188,10 +181,10 @@ class SimpleRelocatorTest { } protected static RelocatePathContext pathContext(String path) { - return RelocatePathContext.builder().path(path).stats(stats).build() + return RelocatePathContext.builder().path(path).build() } protected static RelocateClassContext classContext(String className) { - return RelocateClassContext.builder().className(className).stats(stats).build() + return RelocateClassContext.builder().className(className).build() } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy index 0490305e1..9a4b2b42b 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy @@ -13,7 +13,6 @@ import static org.junit.jupiter.api.Assertions.* class ApacheNoticeResourceTransformerTest extends TransformerTestSupport { private static final String NOTICE_RESOURCE = "META-INF/NOTICE" - private static ShadowStats stats static { setupTurkishLocale() @@ -22,7 +21,6 @@ class ApacheNoticeResourceTransformerTest extends TransformerTestSupport emptyList = Collections.emptyList() - transformer.transform(TransformerContext.builder().path(NOTICE_RESOURCE).inputStream(noticeInputStream).relocators(emptyList).stats(stats).build()) + def transformerContext = TransformerContext.builder() + .path(NOTICE_RESOURCE) + .inputStream(noticeInputStream) + .build() + transformer.transform(transformerContext) } catch (NullPointerException ignored) { fail("Null pointer should not be thrown when no parameters are set.") diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy index 537feba60..840969b36 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy @@ -1,7 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import org.codehaus.plexus.util.IOUtil import org.custommonkey.xmlunit.Diff import org.custommonkey.xmlunit.XMLAssert @@ -13,12 +11,9 @@ import org.junit.jupiter.api.Test * Modified from org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformerTest.java */ class ComponentsXmlResourceTransformerTest extends TransformerTestSupport { - private static ShadowStats stats - @BeforeEach void setUp() { transformer = new ComponentsXmlResourceTransformer() - stats = new ShadowStats() } @Test @@ -29,22 +24,18 @@ class ComponentsXmlResourceTransformerTest extends TransformerTestSupport emptyList()) - .stats(stats) - .build()) + .inputStream(requireResourceAsStream("components-1.xml")) + .build() + ) transformer.transform( TransformerContext.builder() .path("components-1.xml") - .inputStream(getClass().getResourceAsStream("/components-2.xml")) - .relocators(Collections. emptyList()) - .stats(stats) - .build()) + .inputStream(requireResourceAsStream("components-2.xml")) + .build() + ) Diff diff = XMLUnit.compareXML( - IOUtil.toString(getClass().getResourceAsStream("/components-expected.xml"), "UTF-8"), - IOUtil.toString(transformer.getTransformedResource(), "UTF-8")) - //assertEquals( IOUtil.toString( getClass().getResourceAsStream( "/components-expected.xml" ), "UTF-8" ), - // IOUtil.toString( transformer.getTransformedResource(), "UTF-8" ).replaceAll("\r\n", "\n") ) + IOUtil.toString(requireResourceAsStream("components-expected.xml"), "UTF-8"), + IOUtil.toString(transformer.transformedResource, "UTF-8")) XMLAssert.assertXMLIdentical(diff, true) } } diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy index 48719fce6..6db9336f5 100644 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy +++ b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy @@ -15,7 +15,6 @@ import static org.junit.jupiter.api.Assertions.* * Test for {@link ManifestAppenderTransformer}. */ class ManifestAppenderTransformerTest extends TransformerTestSupport { - private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF" @BeforeEach void setUp() { @@ -53,7 +52,7 @@ class ManifestAppenderTransformerTest extends TransformerTestSupport emptyList(), new ShadowStats())) + transform(new TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME))) } def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") @@ -83,9 +82,9 @@ class ManifestAppenderTransformerTest extends TransformerTestSupport emptyList(), new ShadowStats())) + transformer.transform(new TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME))) def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") def fileOutputStream = new FileOutputStream(testableZipFile) @@ -114,8 +113,4 @@ class ManifestAppenderTransformerTest extends TransformerTestSupport { - private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF" @BeforeEach void setUp() { @@ -23,7 +20,7 @@ final class PropertiesFileTransformerTest extends TransformerTestSupport emptyList(), new ShadowStats())) + transformer.transform(new TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME))) def testableZipFile = doTransformAndGetTransformedFile(transformer, false) def targetLines = readFrom(testableZipFile, MANIFEST_NAME) @@ -47,7 +44,7 @@ final class PropertiesFileTransformerTest extends TransformerTestSupport emptyList(), new ShadowStats())) + transformer.transform(new TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME))) def firstRunTransformedFile = doTransformAndGetTransformedFile(transformer, true) def firstRunTargetLines = readFrom(firstRunTransformedFile, MANIFEST_NAME) @@ -87,8 +84,4 @@ final class PropertiesFileTransformerTest extends TransformerTestSupport { protected static T transformer protected static final def objectFactory = ProjectBuilder.builder().build().objects + protected static final String MANIFEST_NAME = "META-INF/MANIFEST.MF" protected static FileTreeElement getFileElement(String path) { return new DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) @@ -20,4 +21,8 @@ abstract class TransformerTestSupport { protected static setupTurkishLocale() { Locale.setDefault(new Locale("tr")) } + + protected InputStream requireResourceAsStream(String resource) { + this.class.classLoader.getResourceAsStream(resource) + } } From c73407bccd94b35feb5dafd940b0bf17e04fc316 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 10:20:57 -0500 Subject: [PATCH 057/941] Migrate unit tests to Kotlin (#1060) * Intro assertk and lints * integrationTestImplementation main sourceSet * Migrate SimpleRelocatorParameterTest * Migrate SimpleRelocatorTest * Migrate ApacheLicenseResourceTransformerTest * Fix merge * Migrate AppendingTransformerTest * Migrate XmlAppendingTransformerTest * Migrate ApacheNoticeResourceTransformerTest * Fix merge * Fix merge * Move packages * Migrate ManifestAppenderTransformerTest * Migrate PropertiesFileTransformerTest * Migrate ComponentsXmlResourceTransformerTest * Move sources into test source set * Rename groovy folder to kotlin * Mark internal * Change suffixes to .kt * Replace IOUtil * Remove redundant throwing * Remove SimpleRelocatorParameterTest as it's outdated * Cleanups * Rename setUp to setup * Cleanups * Migrate java.io.File usages * Reuse doTransformAndGetTransformedPath * Cleanups * Disable lintAnalyzeJvmTest for some devices --- .editorconfig | 3 - build.gradle.kts | 10 + gradle/libs.versions.toml | 2 + src/funcTest/.editorconfig | 2 + .../ComponentsXmlResourceTransformer.kt | 5 +- src/test/.editorconfig | 4 - .../SimpleRelocatorParameterTest.groovy | 30 --- .../relocation/SimpleRelocatorTest.groovy | 190 ------------------ ...pacheLicenseResourceTransformerTest.groovy | 32 --- ...ApacheNoticeResourceTransformerTest.groovy | 84 -------- .../AppendingTransformerTest.groovy | 32 --- ...omponentsXmlResourceTransformerTest.groovy | 41 ---- .../ManifestAppenderTransformerTest.groovy | 116 ----------- .../PropertiesFileTransformerTest.groovy | 87 -------- .../TransformerTestSupport.groovy | 28 --- .../XmlAppendingTransformerTest.groovy | 31 --- .../shadow/relocation/SimpleRelocatorTest.kt | 189 +++++++++++++++++ .../ApacheLicenseResourceTransformerTest.kt | 32 +++ .../ApacheNoticeResourceTransformerTest.kt | 85 ++++++++ .../transformers/AppendingTransformerTest.kt | 31 +++ .../ComponentsXmlResourceTransformerTest.kt | 41 ++++ .../ManifestAppenderTransformerTest.kt | 79 ++++++++ .../PropertiesFileTransformerTest.kt | 56 ++++++ .../transformers/TransformerTestSupport.kt | 59 ++++++ .../XmlAppendingTransformerTest.kt | 28 +++ 25 files changed, 617 insertions(+), 680 deletions(-) create mode 100755 src/funcTest/.editorconfig delete mode 100755 src/test/.editorconfig delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy delete mode 100644 src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt diff --git a/.editorconfig b/.editorconfig index 97c1b8453..f36ddfdde 100755 --- a/.editorconfig +++ b/.editorconfig @@ -7,9 +7,6 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[*.{groovy,java}] -indent_size = 4 - [*.{kt,kts}] ij_kotlin_imports_layout = * ij_kotlin_allow_trailing_comma = true diff --git a/build.gradle.kts b/build.gradle.kts index 82be39fcb..58b4ff7d4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -76,6 +76,7 @@ dependencies { testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) + testImplementation(libs.assertk) testImplementation(libs.xmlunit) testImplementation(libs.apache.commonsLang) testRuntimeOnly(libs.junit.platformLauncher) @@ -87,6 +88,7 @@ dependencies { funcTestImplementation(sourceSets.main.get().output) lintChecks(libs.androidx.gradlePluginLints) + lintChecks(libs.assertk.lint) } val integrationTest by tasks.registering(Test::class) { @@ -130,6 +132,14 @@ tasks.withType().configureEach { ) } +tasks.whenTaskAdded { + if (name == "lintAnalyzeJvmTest") { + // This task often fails on Windows CI devices. + enabled = !providers.systemProperty("os.name").get().startsWith("Windows") && + !providers.environmentVariable("CI").isPresent + } +} + tasks.register("release") { dependsOn( tasks.publish, diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7d3d1d228..406bfe1d4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,6 +11,7 @@ plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha02" +assertk-lint = "com.jzbrooks:assertk-lint:1.3.0" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.4.1" @@ -18,6 +19,7 @@ spock = "org.spockframework:spock-core:2.3-groovy-3.0" junit-bom = "org.junit:junit-bom:5.11.3" junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" } junit-platformLauncher = { module = "org.junit.platform:junit-platform-launcher" } +assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin = "org.jetbrains.kotlin.jvm:2.1.0" diff --git a/src/funcTest/.editorconfig b/src/funcTest/.editorconfig new file mode 100755 index 000000000..3c75e2d26 --- /dev/null +++ b/src/funcTest/.editorconfig @@ -0,0 +1,2 @@ +[*.{groovy,java}] +indent_size = 4 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index 345cfaa44..8c273e6e7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -13,6 +13,7 @@ import org.codehaus.plexus.util.xml.Xpp3Dom import org.codehaus.plexus.util.xml.Xpp3DomBuilder import org.codehaus.plexus.util.xml.Xpp3DomWriter import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Internal /** * A resource processor that aggregates plexus `components.xml` files. @@ -89,8 +90,8 @@ public open class ComponentsXmlResourceTransformer : Transformer { override fun hasTransformedResource(): Boolean = components.isNotEmpty() - @get:Throws(IOException::class) - private val transformedResource: ByteArray + @get:Internal + internal val transformedResource: ByteArray get() { val os = ByteArrayOutputStream(1024 * 4) XmlStreamWriter(os).use { writer -> diff --git a/src/test/.editorconfig b/src/test/.editorconfig deleted file mode 100755 index a54a0eb0d..000000000 --- a/src/test/.editorconfig +++ /dev/null @@ -1,4 +0,0 @@ -root = true - -[*.{groovy,java}] -indent_size = 2 diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy deleted file mode 100644 index 28bd0a412..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorParameterTest.groovy +++ /dev/null @@ -1,30 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -import org.junit.jupiter.api.Test - -import static org.junit.jupiter.api.Assertions.fail - -/** - * Modified from org.apache.maven.plugins.shade.relocation.SimpleRelocatorParameterTest.java - */ -class SimpleRelocatorParameterTest { - - @Test - void testThatNullPatternInConstructorShouldNotThrowNullPointerException() { - constructThenFailOnNullPointerException(null, "") - } - - @Test - void testThatNullShadedPatternInConstructorShouldNotThrowNullPointerException() { - constructThenFailOnNullPointerException("", null) - } - - private static void constructThenFailOnNullPointerException(String pattern, String shadedPattern) { - try { - new SimpleRelocator(pattern, shadedPattern) - } - catch (NullPointerException ignored) { - fail("Constructor should not throw null pointer exceptions") - } - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy deleted file mode 100644 index 1373c9ed5..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.groovy +++ /dev/null @@ -1,190 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -import static org.junit.jupiter.api.Assertions.assertEquals - -/** - * Modified from org.apache.maven.plugins.shade.relocation.SimpleRelocatorTest.java - */ -class SimpleRelocatorTest { - - @Test - void testCanRelocatePath() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org.foo") - assertEquals(true, relocator.canRelocatePath("org/foo/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/bar/Class.class")) - assertEquals(false, relocator.canRelocatePath("com/foo/bar/Class")) - assertEquals(false, relocator.canRelocatePath("com/foo/bar/Class.class")) - assertEquals(false, relocator.canRelocatePath("org/Foo/Class")) - assertEquals(false, relocator.canRelocatePath("org/Foo/Class.class")) - - // Verify paths starting with '/' - assertEquals(false, relocator.canRelocatePath("/org/Foo/Class")) - assertEquals(false, relocator.canRelocatePath("/org/Foo/Class.class")) - - relocator = new SimpleRelocator("org.foo", null, null, - List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) - assertEquals(true, relocator.canRelocatePath("org/foo/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/Class.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/excluded")) - assertEquals(false, relocator.canRelocatePath("org/foo/Excluded")) - assertEquals(false, relocator.canRelocatePath("org/foo/Excluded.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/public")) - assertEquals(false, relocator.canRelocatePath("org/foo/public/Class")) - assertEquals(false, relocator.canRelocatePath("org/foo/public/Class.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/public/sub")) - assertEquals(true, relocator.canRelocatePath("org/foo/public/sub/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/publicRELOC/Class")) - assertEquals(true, relocator.canRelocatePath("org/foo/PrivateStuff")) - assertEquals(true, relocator.canRelocatePath("org/foo/PrivateStuff.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/PublicStuff")) - assertEquals(false, relocator.canRelocatePath("org/foo/PublicStuff.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/PublicUtilStuff")) - assertEquals(false, relocator.canRelocatePath("org/foo/PublicUtilStuff.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/Class")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/Class.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class")) - assertEquals(false, relocator.canRelocatePath("org/foo/recurse/sub/Class.class")) - - // Verify edge cases - relocator = new SimpleRelocator("org.f") - assertEquals(false, relocator.canRelocatePath("")) // Empty path - assertEquals(false, relocator.canRelocatePath(".class")) // only .class - assertEquals(false, relocator.canRelocatePath("te")) // shorter than path pattern - assertEquals(false, relocator.canRelocatePath("test")) // shorter than path pattern with / - assertEquals(true, relocator.canRelocatePath("org/f")) // equal to path pattern - assertEquals(true, relocator.canRelocatePath("/org/f")) // equal to path pattern with / - } - - @Test - void testCanRelocatePathWithRegex() { - SimpleRelocator relocator - - // Include with Regex - relocator = new SimpleRelocator("org.foo", null, Collections.singletonList("%regex[org/foo/R(\\\$.*)?\$]"), null) - assertEquals(true, relocator.canRelocatePath("org/foo/R.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/R\$string.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/R\$layout.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/Recording/R.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/Recording.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/bar/R\$string.class")) - assertEquals(false, relocator.canRelocatePath("org/R.class")) - assertEquals(false, relocator.canRelocatePath("org/R\$string.class")) - - // Exclude with Regex - relocator = new SimpleRelocator("org.foo") - relocator.exclude("%regex[org/foo/.*Factory[0-9].*]") - assertEquals(true, relocator.canRelocatePath("org/foo/Factory.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/FooFactoryMain.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/BarFactory.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/Factory0.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/FooFactory1Main.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/BarFactory2.class")) - - // Include with Regex and normal pattern - relocator = new SimpleRelocator("org.foo", null, - List.of("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*")) - assertEquals(true, relocator.canRelocatePath("org/foo/Factory1.class")) - assertEquals(true, relocator.canRelocatePath("org/foo/public/Bar.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/Factory.class")) - assertEquals(false, relocator.canRelocatePath("org/foo/R.class")) - } - - @Test - void testCanRelocateClass() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org.foo") - assertEquals(true, relocator.canRelocateClass("org.foo.Class")) - assertEquals(true, relocator.canRelocateClass("org.foo.bar.Class")) - assertEquals(false, relocator.canRelocateClass("com.foo.bar.Class")) - assertEquals(false, relocator.canRelocateClass("org.Foo.Class")) - - relocator = new SimpleRelocator("org.foo", null, null, - List.of("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff")) - assertEquals(true, relocator.canRelocateClass("org.foo.Class")) - assertEquals(true, relocator.canRelocateClass("org.foo.excluded")) - assertEquals(false, relocator.canRelocateClass("org.foo.Excluded")) - assertEquals(false, relocator.canRelocateClass("org.foo.public")) - assertEquals(false, relocator.canRelocateClass("org.foo.public.Class")) - assertEquals(false, relocator.canRelocateClass("org.foo.public.sub")) - assertEquals(true, relocator.canRelocateClass("org.foo.public.sub.Class")) - assertEquals(true, relocator.canRelocateClass("org.foo.publicRELOC.Class")) - assertEquals(true, relocator.canRelocateClass("org.foo.PrivateStuff")) - assertEquals(false, relocator.canRelocateClass("org.foo.PublicStuff")) - assertEquals(false, relocator.canRelocateClass("org.foo.PublicUtilStuff")) - assertEquals(false, relocator.canRelocateClass("org.foo.recurse")) - assertEquals(false, relocator.canRelocateClass("org.foo.recurse.Class")) - assertEquals(false, relocator.canRelocateClass("org.foo.recurse.sub")) - assertEquals(false, relocator.canRelocateClass("org.foo.recurse.sub.Class")) - } - - @Test - void testCanRelocateRawString() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org/foo", null, null, null, true) - assertEquals(true, relocator.canRelocatePath("(I)org/foo/bar/Class")) - - relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", null, null, null, true) - assertEquals(true, relocator.canRelocatePath("META-INF/org.foo.xml")) - } - - //MSHADE-119, make sure that the easy part of this works. - @Test - void testCanRelocateAbsClassPath() { - SimpleRelocator relocator = new SimpleRelocator("org.apache.velocity", "org.apache.momentum") - assertEquals("/org/apache/momentum/mass.properties", relocator.relocatePath(pathContext("/org/apache/velocity/mass.properties"))) - - } - - @Test - void testRelocatePath() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org.foo") - assertEquals("hidden/org/foo/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) - - relocator = new SimpleRelocator("org.foo", "private.stuff") - assertEquals("private/stuff/bar/Class.class", relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) - } - - @Test - void testRelocateClass() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("org.foo") - assertEquals("hidden.org.foo.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) - - relocator = new SimpleRelocator("org.foo", "private.stuff") - assertEquals("private.stuff.bar.Class", relocator.relocateClass(classContext("org.foo.bar.Class"))) - } - - @Test - void testRelocateRawString() { - SimpleRelocator relocator - - relocator = new SimpleRelocator("Lorg/foo", "Lhidden/org/foo", null, null, true) - assertEquals("(I)Lhidden/org/foo/bar/Class", relocator.relocatePath(pathContext("(I)Lorg/foo/bar/Class"))) - - relocator = new SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", null, null, true) - assertEquals("META-INF/hidden.org.foo.xml", relocator.relocatePath(pathContext("META-INF/org.foo.xml"))) - } - - protected static RelocatePathContext pathContext(String path) { - return RelocatePathContext.builder().path(path).build() - } - - protected static RelocateClassContext classContext(String className) { - return RelocateClassContext.builder().className(className).build() - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy deleted file mode 100644 index 27117c51c..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.groovy +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -import static org.junit.jupiter.api.Assertions.assertFalse -import static org.junit.jupiter.api.Assertions.assertTrue - -/** - * Modified from org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformerTest.java - */ -class ApacheLicenseResourceTransformerTest extends TransformerTestSupport { - - static { - setupTurkishLocale() - } - - @BeforeEach - void setUp() { - transformer = new ApacheLicenseResourceTransformer() - } - - @Test - void testCanTransformResource() { - assertTrue(transformer.canTransformResource(getFileElement("META-INF/LICENSE"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/LICENSE.TXT"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/License.txt"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/LICENSE.md"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/License.md"))) - assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy deleted file mode 100644 index 9a4b2b42b..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.groovy +++ /dev/null @@ -1,84 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -import static org.junit.jupiter.api.Assertions.* - -/** - * Modified from org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerParameterTests.java - */ -class ApacheNoticeResourceTransformerTest extends TransformerTestSupport { - - private static final String NOTICE_RESOURCE = "META-INF/NOTICE" - - static { - setupTurkishLocale() - } - - @BeforeEach - void setUp() { - transformer = new ApacheNoticeResourceTransformer(objectFactory) - } - - @Test - void testCanTransformResource() { - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.TXT"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.txt"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/NOTICE.md"))) - assertTrue(transformer.canTransformResource(getFileElement("META-INF/Notice.md"))) - assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenNoInput() { - processAndFailOnNullPointer("") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenNoLinesOfInput() { - processAndFailOnNullPointer("Some notice text") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenOneLineOfInput() { - processAndFailOnNullPointer("Some notice text\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenTwoLinesOfInput() { - processAndFailOnNullPointer("Some notice text\nSome notice text\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenLineStartsWithSlashSlash() { - processAndFailOnNullPointer("Some notice text\n//Some notice text\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenLineIsSlashSlash() { - processAndFailOnNullPointer("//\n") - } - - @Test - void testNoParametersShouldNotThrowNullPointerWhenLineIsEmpty() { - processAndFailOnNullPointer("\n") - } - - private static void processAndFailOnNullPointer(final String noticeText) { - try { - final ByteArrayInputStream noticeInputStream = new ByteArrayInputStream(noticeText.getBytes()) - def transformerContext = TransformerContext.builder() - .path(NOTICE_RESOURCE) - .inputStream(noticeInputStream) - .build() - transformer.transform(transformerContext) - } - catch (NullPointerException ignored) { - fail("Null pointer should not be thrown when no parameters are set.") - } - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy deleted file mode 100644 index f9d4b8f59..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.groovy +++ /dev/null @@ -1,32 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -import static org.junit.jupiter.api.Assertions.assertFalse -import static org.junit.jupiter.api.Assertions.assertTrue - -/** - * Modified from org.apache.maven.plugins.shade.resource.AppendingTransformerTest.java - */ -class AppendingTransformerTest extends TransformerTestSupport { - - static { - setupTurkishLocale() - } - - @BeforeEach - void setUp() { - transformer = new AppendingTransformer(objectFactory) - } - - @Test - void testCanTransformResource() { - transformer.resource.set("abcdefghijklmnopqrstuvwxyz") - - assertTrue(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))) - assertTrue(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) - assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) - } - -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy deleted file mode 100644 index 840969b36..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.groovy +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.codehaus.plexus.util.IOUtil -import org.custommonkey.xmlunit.Diff -import org.custommonkey.xmlunit.XMLAssert -import org.custommonkey.xmlunit.XMLUnit -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -/** - * Modified from org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformerTest.java - */ -class ComponentsXmlResourceTransformerTest extends TransformerTestSupport { - @BeforeEach - void setUp() { - transformer = new ComponentsXmlResourceTransformer() - } - - @Test - void testConfigurationMerging() { - - XMLUnit.setNormalizeWhitespace(true) - - transformer.transform( - TransformerContext.builder() - .path("components-1.xml") - .inputStream(requireResourceAsStream("components-1.xml")) - .build() - ) - transformer.transform( - TransformerContext.builder() - .path("components-1.xml") - .inputStream(requireResourceAsStream("components-2.xml")) - .build() - ) - Diff diff = XMLUnit.compareXML( - IOUtil.toString(requireResourceAsStream("components-expected.xml"), "UTF-8"), - IOUtil.toString(transformer.transformedResource, "UTF-8")) - XMLAssert.assertXMLIdentical(diff, true) - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy deleted file mode 100644 index 6db9336f5..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.groovy +++ /dev/null @@ -1,116 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import org.apache.tools.zip.ZipOutputStream -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -import java.util.zip.ZipFile - -import static java.util.Arrays.asList -import static org.junit.jupiter.api.Assertions.* - -/** - * Test for {@link ManifestAppenderTransformer}. - */ -class ManifestAppenderTransformerTest extends TransformerTestSupport { - - @BeforeEach - void setUp() { - transformer = new ManifestAppenderTransformer(objectFactory) - } - - @Test - void testCanTransformResource() { - transformer.with { - append('Name', 'org/foo/bar/') - append('Sealed', true) - } - - assertTrue(transformer.canTransformResource(getFileElement(MANIFEST_NAME))) - assertTrue(transformer.canTransformResource(getFileElement(MANIFEST_NAME.toLowerCase()))) - } - - @Test - void testHasTransformedResource() { - transformer.append('Tag', 'Something') - - assertTrue(transformer.hasTransformedResource()) - } - - @Test - void testHasNotTransformedResource() { - assertFalse(transformer.hasTransformedResource()) - } - - @Test - void testTransformation() { - transformer.with { - append('Name', 'org/foo/bar/') - append('Sealed', true) - append('Name', 'com/example/') - append('Sealed', false) - - transform(new TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME))) - } - - def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") - def fileOutputStream = new FileOutputStream(testableZipFile) - def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) - def zipOutputStream = new ZipOutputStream(bufferedOutputStream) - - try { - transformer.modifyOutputStream(zipOutputStream, true) - } finally { - zipOutputStream.close() - } - - def targetLines = readFrom(testableZipFile, MANIFEST_NAME) - assertFalse(targetLines.isEmpty()) - assertTrue(targetLines.size() > 4) - - def trailer = targetLines.with { subList(size() - 5, size()) } - assertEquals(asList( - "Name: org/foo/bar/", - "Sealed: true", - "Name: com/example/", - "Sealed: false", - ""), trailer - ) - } - - @Test - void testNoTransformation() { - def sourceLines = requireResourceAsStream(MANIFEST_NAME).readLines() - - transformer.transform(new TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME))) - - def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") - def fileOutputStream = new FileOutputStream(testableZipFile) - def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) - def zipOutputStream = new ZipOutputStream(bufferedOutputStream) - - try { - transformer.modifyOutputStream(zipOutputStream, true) - } finally { - zipOutputStream.close() - } - def targetLines = readFrom(testableZipFile, MANIFEST_NAME) - - assertEquals(sourceLines, targetLines) - } - - static List readFrom(File jarFile, String resourceName) { - def zip = new ZipFile(jarFile) - try { - def entry = zip.getEntry(resourceName) - if (!entry) { - return Collections.emptyList() - } - return zip.getInputStream(entry).readLines() - } finally { - zip.close() - } - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy deleted file mode 100644 index c69ce09f2..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.groovy +++ /dev/null @@ -1,87 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.apache.tools.zip.ZipOutputStream -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -import java.util.zip.ZipFile - -import static org.junit.jupiter.api.Assertions.* - -/** - * Test for {@link PropertiesFileTransformer}. - */ -final class PropertiesFileTransformerTest extends TransformerTestSupport { - - @BeforeEach - void setUp() { - transformer = new PropertiesFileTransformer(objectFactory) - } - - @Test - void testHasTransformedResource() { - transformer.transform(new TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME))) - - assertTrue(transformer.hasTransformedResource()) - } - - @Test - void testHasNotTransformedResource() { - assertFalse(transformer.hasTransformedResource()) - } - - @Test - void testTransformation() { - transformer.transform(new TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME))) - - def testableZipFile = doTransformAndGetTransformedFile(transformer, false) - def targetLines = readFrom(testableZipFile, MANIFEST_NAME) - - assertFalse(targetLines.isEmpty()) - - assertTrue(targetLines.contains("Manifest-Version=1.0")) - } - - @Test - void testTransformationPropertiesAreReproducible() { - transformer.transform(new TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME))) - - def firstRunTransformedFile = doTransformAndGetTransformedFile(transformer, true) - def firstRunTargetLines = readFrom(firstRunTransformedFile, MANIFEST_NAME) - - Thread.sleep(1000) // wait for 1sec to ensure timestamps in properties would change - - def secondRunTransformedFile = doTransformAndGetTransformedFile(transformer, true) - def secondRunTargetLines = readFrom(secondRunTransformedFile, MANIFEST_NAME) - - assertEquals(firstRunTargetLines, secondRunTargetLines) - } - - static File doTransformAndGetTransformedFile(final PropertiesFileTransformer transformer, final boolean preserveFileTimestamps) { - def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") - def fileOutputStream = new FileOutputStream(testableZipFile) - def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) - def zipOutputStream = new ZipOutputStream(bufferedOutputStream) - - try { - transformer.modifyOutputStream(zipOutputStream, preserveFileTimestamps) - } finally { - zipOutputStream.close() - } - - return testableZipFile - } - - static List readFrom(File jarFile, String resourceName) { - def zip = new ZipFile(jarFile) - try { - def entry = zip.getEntry(resourceName) - if (!entry) { - return Collections.emptyList() - } - return zip.getInputStream(entry).readLines() - } finally { - zip.close() - } - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy deleted file mode 100644 index f88d5492b..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.groovy +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.gradle.api.file.FileTreeElement -import org.gradle.api.file.RelativePath -import org.gradle.api.internal.file.DefaultFileTreeElement -import org.gradle.testfixtures.ProjectBuilder - -abstract class TransformerTestSupport { - protected static T transformer - protected static final def objectFactory = ProjectBuilder.builder().build().objects - protected static final String MANIFEST_NAME = "META-INF/MANIFEST.MF" - - protected static FileTreeElement getFileElement(String path) { - return new DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) - } - - /** - * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime - * choice to test for improper case-less string comparisons. - */ - protected static setupTurkishLocale() { - Locale.setDefault(new Locale("tr")) - } - - protected InputStream requireResourceAsStream(String resource) { - this.class.classLoader.getResourceAsStream(resource) - } -} diff --git a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy b/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy deleted file mode 100644 index 71c02736c..000000000 --- a/src/test/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.groovy +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -import static org.junit.jupiter.api.Assertions.assertFalse -import static org.junit.jupiter.api.Assertions.assertTrue - -/** - * Modified from org.apache.maven.plugins.shade.resource.XmlAppendingTransformerTest.java - */ -class XmlAppendingTransformerTest extends TransformerTestSupport { - - static { - setupTurkishLocale() - } - - @BeforeEach - void setUp() { - transformer = new XmlAppendingTransformer(objectFactory) - } - - @Test - void testCanTransformResource() { - transformer.resource.set("abcdefghijklmnopqrstuvwxyz") - - assertTrue(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))) - assertTrue(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))) - assertFalse(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))) - } -} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt new file mode 100644 index 000000000..ad8d042b9 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -0,0 +1,189 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import org.junit.jupiter.api.Test + +/** + * Modified from [org.apache.maven.plugins.shade.relocation.SimpleRelocatorTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java). + * + * @author John Engelman + */ +class SimpleRelocatorTest { + + @Test + fun testCanRelocatePath() { + var relocator = SimpleRelocator("org.foo") + assertThat(relocator.canRelocatePath("org/foo/Class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/bar/Class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/bar/Class.class")).isTrue() + assertThat(relocator.canRelocatePath("com/foo/bar/Class")).isFalse() + assertThat(relocator.canRelocatePath("com/foo/bar/Class.class")).isFalse() + assertThat(relocator.canRelocatePath("org/Foo/Class")).isFalse() + assertThat(relocator.canRelocatePath("org/Foo/Class.class")).isFalse() + + // Verify paths starting with '/' + assertThat(relocator.canRelocatePath("/org/Foo/Class")).isFalse() + assertThat(relocator.canRelocatePath("/org/Foo/Class.class")).isFalse() + + relocator = SimpleRelocator( + "org.foo", + excludes = listOf("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff"), + ) + assertThat(relocator.canRelocatePath("org/foo/Class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/excluded")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/Excluded")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/Excluded.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/public")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/public/Class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/public/Class.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/public/sub")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/public/sub/Class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/publicRELOC/Class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/PrivateStuff")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/PrivateStuff.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/PublicStuff")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/PublicStuff.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/PublicUtilStuff")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/PublicUtilStuff.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/recurse")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/recurse/Class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/recurse/Class.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/recurse/sub")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/recurse/sub/Class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/recurse/sub/Class.class")).isFalse() + + // Verify edge cases + relocator = SimpleRelocator("org.f") + assertThat(relocator.canRelocatePath("")).isFalse() // Empty path + assertThat(relocator.canRelocatePath(".class")).isFalse() // only .class + assertThat(relocator.canRelocatePath("te")).isFalse() // shorter than path pattern + assertThat(relocator.canRelocatePath("test")).isFalse() // shorter than path pattern with / + assertThat(relocator.canRelocatePath("org/f")).isTrue() // equal to path pattern + assertThat(relocator.canRelocatePath("/org/f")).isTrue() // equal to path pattern with / + } + + @Test + fun testCanRelocatePathWithRegex() { + // Include with Regex + var relocator = SimpleRelocator("org.foo", includes = listOf("%regex[org/foo/R(\\\$.*)?\$]")) + assertThat(relocator.canRelocatePath("org/foo/R.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/R\$string.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/R\$layout.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/Recording/R.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/Recording.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/bar/R\$string.class")).isFalse() + assertThat(relocator.canRelocatePath("org/R.class")).isFalse() + assertThat(relocator.canRelocatePath("org/R\$string.class")).isFalse() + + // Exclude with Regex + relocator = SimpleRelocator("org.foo") + relocator.exclude("%regex[org/foo/.*Factory[0-9].*]") + assertThat(relocator.canRelocatePath("org/foo/Factory.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/FooFactoryMain.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/BarFactory.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/Factory0.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/FooFactory1Main.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/BarFactory2.class")).isFalse() + + // Include with Regex and normal pattern + relocator = SimpleRelocator( + "org.foo", + includes = listOf("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*"), + ) + assertThat(relocator.canRelocatePath("org/foo/Factory1.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/public/Bar.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/Factory.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/R.class")).isFalse() + } + + @Test + fun testCanRelocateClass() { + var relocator = SimpleRelocator("org.foo") + assertThat(relocator.canRelocateClass("org.foo.Class")).isTrue() + assertThat(relocator.canRelocateClass("org.foo.bar.Class")).isTrue() + assertThat(relocator.canRelocateClass("com.foo.bar.Class")).isFalse() + assertThat(relocator.canRelocateClass("org.Foo.Class")).isFalse() + + relocator = SimpleRelocator( + "org.foo", + excludes = listOf("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff"), + ) + assertThat(relocator.canRelocateClass("org.foo.Class")).isTrue() + assertThat(relocator.canRelocateClass("org.foo.excluded")).isTrue() + assertThat(relocator.canRelocateClass("org.foo.Excluded")).isFalse() + assertThat(relocator.canRelocateClass("org.foo.public")).isFalse() + assertThat(relocator.canRelocateClass("org.foo.public.Class")).isFalse() + assertThat(relocator.canRelocateClass("org.foo.public.sub")).isFalse() + assertThat(relocator.canRelocateClass("org.foo.public.sub.Class")).isTrue() + assertThat(relocator.canRelocateClass("org.foo.publicRELOC.Class")).isTrue() + assertThat(relocator.canRelocateClass("org.foo.PrivateStuff")).isTrue() + assertThat(relocator.canRelocateClass("org.foo.PublicStuff")).isFalse() + assertThat(relocator.canRelocateClass("org.foo.PublicUtilStuff")).isFalse() + assertThat(relocator.canRelocateClass("org.foo.recurse")).isFalse() + assertThat(relocator.canRelocateClass("org.foo.recurse.Class")).isFalse() + assertThat(relocator.canRelocateClass("org.foo.recurse.sub")).isFalse() + assertThat(relocator.canRelocateClass("org.foo.recurse.sub.Class")).isFalse() + } + + @Test + fun testCanRelocateRawString() { + var relocator = SimpleRelocator("org/foo", _rawString = true) + assertThat(relocator.canRelocatePath("(I)org/foo/bar/Class")).isTrue() + + relocator = SimpleRelocator("^META-INF/org.foo.xml\$", _rawString = true) + assertThat(relocator.canRelocatePath("META-INF/org.foo.xml")).isTrue() + } + + @Test + fun testCanRelocateAbsClassPath() { + val relocator = SimpleRelocator("org.apache.velocity", "org.apache.momentum") + assertThat(relocator.relocatePath(pathContext("/org/apache/velocity/mass.properties"))) + .isEqualTo("/org/apache/momentum/mass.properties") + } + + @Test + fun testRelocatePath() { + var relocator = SimpleRelocator("org.foo") + assertThat(relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) + .isEqualTo("hidden/org/foo/bar/Class.class") + + relocator = SimpleRelocator("org.foo", "private.stuff") + assertThat(relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) + .isEqualTo("private/stuff/bar/Class.class") + } + + @Test + fun testRelocateClass() { + var relocator = SimpleRelocator("org.foo") + assertThat(relocator.relocateClass(classContext())) + .isEqualTo("hidden.org.foo.bar.Class") + + relocator = SimpleRelocator("org.foo", "private.stuff") + assertThat(relocator.relocateClass(classContext())) + .isEqualTo("private.stuff.bar.Class") + } + + @Test + fun testRelocateRawString() { + var relocator = SimpleRelocator("Lorg/foo", "Lhidden/org/foo", _rawString = true) + assertThat(relocator.relocatePath(pathContext("(I)Lorg/foo/bar/Class"))) + .isEqualTo("(I)Lhidden/org/foo/bar/Class") + + relocator = SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", _rawString = true) + assertThat(relocator.relocatePath(pathContext("META-INF/org.foo.xml"))) + .isEqualTo("META-INF/hidden.org.foo.xml") + } + + private fun pathContext(path: String): RelocatePathContext { + return RelocatePathContext.builder().path(path).build() + } + + private fun classContext(className: String = "org.foo.bar.Class"): RelocateClassContext { + return RelocateClassContext.builder().className(className).build() + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt new file mode 100644 index 000000000..407721fed --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt @@ -0,0 +1,32 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +/** + * Modified from [org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformerTest.java). + */ +class ApacheLicenseResourceTransformerTest : TransformerTestSupport() { + + init { + setupTurkishLocale() + } + + @BeforeEach + fun setup() { + transformer = ApacheLicenseResourceTransformer() + } + + @Test + fun testCanTransformResource() { + assertThat(transformer.canTransformResource(getFileElement("META-INF/LICENSE"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/LICENSE.TXT"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/License.txt"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/LICENSE.md"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/License.md"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))).isFalse() + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt new file mode 100644 index 000000000..b7da3fbb9 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -0,0 +1,85 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import assertk.fail +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +/** + * Modified from [org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerParameterTests.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformerParameterTests.java). + */ +class ApacheNoticeResourceTransformerTest : TransformerTestSupport() { + + init { + setupTurkishLocale() + } + + @BeforeEach + fun setup() { + transformer = ApacheNoticeResourceTransformer(objectFactory) + } + + @Test + fun testCanTransformResource() { + assertThat(transformer.canTransformResource(getFileElement("META-INF/NOTICE"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/NOTICE.TXT"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/Notice.txt"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/NOTICE.md"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/Notice.md"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))).isFalse() + } + + @Test + fun testNoParametersShouldNotThrowNullPointerWhenNoInput() { + processAndFailOnNullPointer("") + } + + @Test + fun testNoParametersShouldNotThrowNullPointerWhenNoLinesOfInput() { + processAndFailOnNullPointer("Some notice text") + } + + @Test + fun testNoParametersShouldNotThrowNullPointerWhenOneLineOfInput() { + processAndFailOnNullPointer("Some notice text\n") + } + + @Test + fun testNoParametersShouldNotThrowNullPointerWhenTwoLinesOfInput() { + processAndFailOnNullPointer("Some notice text\nSome notice text\n") + } + + @Test + fun testNoParametersShouldNotThrowNullPointerWhenLineStartsWithSlashSlash() { + processAndFailOnNullPointer("Some notice text\n//Some notice text\n") + } + + @Test + fun testNoParametersShouldNotThrowNullPointerWhenLineIsSlashSlash() { + processAndFailOnNullPointer("//\n") + } + + @Test + fun testNoParametersShouldNotThrowNullPointerWhenLineIsEmpty() { + processAndFailOnNullPointer("\n") + } + + private fun processAndFailOnNullPointer(noticeText: String) { + try { + transformer.transform( + TransformerContext.builder() + .path(NOTICE_RESOURCE) + .inputStream(noticeText.toByteArray().inputStream()) + .build(), + ) + } catch (ignored: NullPointerException) { + fail("Null pointer should not be thrown when no parameters are set.") + } + } + + companion object { + private const val NOTICE_RESOURCE = "META-INF/NOTICE" + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt new file mode 100644 index 000000000..997717220 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -0,0 +1,31 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +/** + * Modified from [org.apache.maven.plugins.shade.resource.AppendingTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/AppendingTransformerTest.java). + */ +class AppendingTransformerTest : TransformerTestSupport() { + + init { + setupTurkishLocale() + } + + @BeforeEach + fun setup() { + transformer = AppendingTransformer(objectFactory) + } + + @Test + fun testCanTransformResource() { + transformer.resource.set("abcdefghijklmnopqrstuvwxyz") + + assertThat(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))).isFalse() + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt new file mode 100644 index 000000000..9045a89b2 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -0,0 +1,41 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isTrue +import org.custommonkey.xmlunit.XMLUnit +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +/** + * Modified from [org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformerTest.java). + */ +class ComponentsXmlResourceTransformerTest : TransformerTestSupport() { + + @BeforeEach + fun setup() { + transformer = ComponentsXmlResourceTransformer() + } + + @Test + fun testConfigurationMerging() { + XMLUnit.setNormalizeWhitespace(true) + + transformer.transform( + TransformerContext.builder() + .path("components-1.xml") + .inputStream(requireResourceAsStream("components-1.xml")) + .build(), + ) + transformer.transform( + TransformerContext.builder() + .path("components-1.xml") + .inputStream(requireResourceAsStream("components-2.xml")) + .build(), + ) + val diff = XMLUnit.compareXML( + requireResourceAsStream("components-expected.xml").bufferedReader().readText(), + transformer.transformedResource.decodeToString(), + ) + assertThat(diff.identical()).isTrue() + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt new file mode 100644 index 000000000..4c5c21714 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -0,0 +1,79 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isGreaterThan +import assertk.assertions.isNotEmpty +import assertk.assertions.isTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class ManifestAppenderTransformerTest : TransformerTestSupport() { + @BeforeEach + fun setup() { + transformer = ManifestAppenderTransformer(objectFactory) + } + + @Test + fun testCanTransformResource() { + with(transformer) { + append("Name", "org/foo/bar/") + append("Sealed", true) + } + + assertThat(transformer.canTransformResource(getFileElement(MANIFEST_NAME))).isTrue() + assertThat(transformer.canTransformResource(getFileElement(MANIFEST_NAME.lowercase()))).isTrue() + } + + @Test + fun testHasTransformedResource() { + transformer.append("Tag", "Something") + + assertThat(transformer.hasTransformedResource()).isTrue() + } + + @Test + fun testHasNotTransformedResource() { + assertThat(transformer.hasTransformedResource()).isFalse() + } + + @Test + fun testTransformation() { + with(transformer) { + append("Name", "org/foo/bar/") + append("Sealed", true) + append("Name", "com/example/") + append("Sealed", false) + + transform(manifestTransformerContext) + } + + val testableZipPath = doTransformAndGetTransformedPath(transformer, true) + + val targetLines = readFrom(testableZipPath) + assertThat(targetLines).isNotEmpty() + assertThat(targetLines.size).isGreaterThan(4) + + val trailer = targetLines.subList(targetLines.size - 5, targetLines.size) + assertThat(trailer).isEqualTo( + listOf( + "Name: org/foo/bar/", + "Sealed: true", + "Name: com/example/", + "Sealed: false", + "", + ), + ) + } + + @Test + fun testNoTransformation() { + val sourceLines = requireResourceAsStream(MANIFEST_NAME).bufferedReader().readLines() + transformer.transform(manifestTransformerContext) + val testableZipPath = doTransformAndGetTransformedPath(transformer, true) + val targetLines = readFrom(testableZipPath) + + assertThat(targetLines).isEqualTo(sourceLines) + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt new file mode 100644 index 000000000..0b8af0815 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -0,0 +1,56 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isNotEmpty +import assertk.assertions.isTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class PropertiesFileTransformerTest : TransformerTestSupport() { + + @BeforeEach + fun setup() { + transformer = PropertiesFileTransformer(objectFactory) + } + + @Test + fun testHasTransformedResource() { + transformer.transform(manifestTransformerContext) + + assertThat(transformer.hasTransformedResource()).isTrue() + } + + @Test + fun testHasNotTransformedResource() { + assertThat(transformer.hasTransformedResource()).isFalse() + } + + @Test + fun testTransformation() { + transformer.transform(manifestTransformerContext) + + val testableZipPath = doTransformAndGetTransformedPath(transformer, false) + val targetLines = readFrom(testableZipPath) + + assertThat(targetLines).isNotEmpty() + assertThat(targetLines).contains("Manifest-Version=1.0") + } + + @Test + fun testTransformationPropertiesAreReproducible() { + transformer.transform(manifestTransformerContext) + + val firstRunTransformedPath = doTransformAndGetTransformedPath(transformer, true) + val firstRunTargetLines = readFrom(firstRunTransformedPath) + + Thread.sleep(1000) // wait for 1sec to ensure timestamps in properties would change + + val secondRunTransformedPath = doTransformAndGetTransformedPath(transformer, true) + val secondRunTargetLines = readFrom(secondRunTransformedPath) + + assertThat(firstRunTargetLines).isEqualTo(secondRunTargetLines) + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt new file mode 100644 index 000000000..8fcd2d84d --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt @@ -0,0 +1,59 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import java.io.InputStream +import java.nio.file.Path +import java.util.Locale +import java.util.zip.ZipFile +import kotlin.io.path.createTempFile +import kotlin.io.path.outputStream +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement +import org.gradle.api.file.RelativePath +import org.gradle.api.internal.file.DefaultFileTreeElement +import org.gradle.testfixtures.ProjectBuilder + +abstract class TransformerTestSupport { + protected lateinit var transformer: T + + protected val manifestTransformerContext: TransformerContext + get() = TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME)) + + protected fun requireResourceAsStream(name: String): InputStream { + return this::class.java.classLoader.getResourceAsStream(name) ?: error("Resource $name not found.") + } + + protected companion object { + const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" + val objectFactory = ProjectBuilder.builder().build().objects + + fun getFileElement(path: String): FileTreeElement { + return DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) + } + + fun readFrom(jarPath: Path, resourceName: String = MANIFEST_NAME): List { + return ZipFile(jarPath.toFile()).use { zip -> + val entry = zip.getEntry(resourceName) ?: return emptyList() + zip.getInputStream(entry).bufferedReader().readLines() + } + } + + fun doTransformAndGetTransformedPath( + transformer: Transformer, + preserveFileTimestamps: Boolean, + ): Path { + val testableZipPath = createTempFile("testable-zip-file-", ".jar") + ZipOutputStream(testableZipPath.outputStream().buffered()).use { zipOutputStream -> + transformer.modifyOutputStream(zipOutputStream, preserveFileTimestamps) + } + return testableZipPath + } + + /** + * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime + * choice to test for improper case-less string comparisons. + */ + fun setupTurkishLocale() { + Locale.setDefault(Locale("tr")) + } + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt new file mode 100644 index 000000000..5bb4b85f4 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -0,0 +1,28 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class XmlAppendingTransformerTest : TransformerTestSupport() { + + init { + setupTurkishLocale() + } + + @BeforeEach + fun setup() { + transformer = XmlAppendingTransformer(objectFactory) + } + + @Test + fun testCanTransformResource() { + transformer.resource.set("abcdefghijklmnopqrstuvwxyz") + + assertThat(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))).isTrue() + assertThat(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))).isFalse() + } +} From 8ddb7c5334d15ef054f37aa9999e7680a55c5b2a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 13:04:00 -0500 Subject: [PATCH 058/941] Add createDefaultFileTreeElement to avoid null checks (#1069) * Add createDefaultFileTreeElement * Impl createDefaultFileTreeElement with dummies * Run updateLintBaseline --- lint-baseline.xml | 67 ++++++++++++++----- .../TransformerSpecSupport.groovy | 1 + .../gradle/plugins/shadow/internal/Utils.kt | 26 +++++++ .../plugins/shadow/tasks/ShadowCopyAction.kt | 4 +- .../transformers/TransformerTestSupport.kt | 4 +- 5 files changed, 81 insertions(+), 21 deletions(-) diff --git a/lint-baseline.xml b/lint-baseline.xml index fd03ccbe4..e8f9336a9 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -41,7 +41,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -50,17 +50,6 @@ message="Avoid using internal Gradle APIs" errorLine1="import org.gradle.api.internal.file.CopyActionProcessingStreamAction" errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - - - - @@ -129,7 +118,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +129,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -151,7 +140,51 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + + + + + + + + + + + + + + + + diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy index e6384cb78..818697e0b 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy @@ -20,6 +20,7 @@ class TransformerSpecSupport extends Specification { } protected static FileTreeElement getFileElement(String path) { + // TODO: this should be replace with `createDefaultFileTreeElement` once this test gets migrated to Kotlin. return new DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index dfd019c8e..0871a3a6c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -1,11 +1,17 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import java.io.File import java.io.InputStream import org.gradle.api.Project import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.RelativePath +import org.gradle.api.internal.file.DefaultFileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.plugins.JavaPlugin import org.gradle.api.provider.Property +import org.gradle.internal.file.Chmod +import org.gradle.internal.file.FileMetadata +import org.gradle.internal.file.Stat /** * Return `runtimeClasspath` or `runtime` configuration. @@ -21,6 +27,19 @@ internal inline fun ObjectFactory.property(defaultValue: T? = } } +/** + * This is used for creating a [DefaultFileTreeElement] with default values. + * [file], [chmod], and [stat] should be non-null, so they are set to dummy values here. + */ +internal fun createDefaultFileTreeElement( + file: File = DummyFile, + relativePath: RelativePath, + chmod: Chmod = DummyChmod, + stat: Stat = DummyStat, +): DefaultFileTreeElement { + return DefaultFileTreeElement(file, relativePath, chmod, stat) +} + @Suppress("NOTHING_TO_INLINE") internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy { return lazy(LazyThreadSafetyMode.NONE, initializer) @@ -33,3 +52,10 @@ internal fun Class<*>.requireResourceAsText(name: String): String { private fun Class<*>.requireResourceAsStream(name: String): InputStream { return getResourceAsStream(name) ?: error("Resource $name not found.") } + +private val DummyFile = File("dummy") +private val DummyChmod = Chmod { _, _ -> error("This is a dummy implementation.") } +private val DummyStat = object : Stat { + override fun getUnixMode(f: File): Int = error("This is a dummy implementation.") + override fun stat(f: File): FileMetadata = error("This is a dummy implementation.") +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 00b5ce71c..657b1ab17 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.impl.RelocatorRemapper import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor +import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext @@ -26,7 +27,6 @@ import org.gradle.api.file.RelativePath import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.CopyActionProcessingStreamAction import org.gradle.api.internal.file.DefaultFilePermissions -import org.gradle.api.internal.file.DefaultFileTreeElement import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.CopyActionProcessingStream import org.gradle.api.internal.file.copy.FileCopyDetailsInternal @@ -429,7 +429,7 @@ public open class ShadowCopyAction internal constructor( override fun getPermissions(): FilePermissions = DefaultFilePermissions(mode) public open fun asFileTreeElement(): FileTreeElement { - return DefaultFileTreeElement(null, RelativePath(!isDirectory, *archivePath.segments), null, null) + return createDefaultFileTreeElement(relativePath = RelativePath(!isDirectory, *archivePath.segments)) } override fun getFile(): File = throw UnsupportedOperationException() diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt index 8fcd2d84d..8ae1f741a 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement import java.io.InputStream import java.nio.file.Path import java.util.Locale @@ -9,7 +10,6 @@ import kotlin.io.path.outputStream import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath -import org.gradle.api.internal.file.DefaultFileTreeElement import org.gradle.testfixtures.ProjectBuilder abstract class TransformerTestSupport { @@ -27,7 +27,7 @@ abstract class TransformerTestSupport { val objectFactory = ProjectBuilder.builder().build().objects fun getFileElement(path: String): FileTreeElement { - return DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) + return createDefaultFileTreeElement(relativePath = RelativePath.parse(true, path)) } fun readFrom(jarPath: Path, resourceName: String = MANIFEST_NAME): List { From a65d4206730c9b9425dd16231db16c84eb661943 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 23:04:37 -0500 Subject: [PATCH 059/941] Move JavaJarExec out of internal (#1072) Users can import the `runShadow` type in their build files. --- api/shadow.api | 6 ++++++ .../gradle/plugins/shadow/ShadowApplicationPlugin.kt | 2 +- .../plugins/shadow/{internal => tasks}/JavaJarExec.kt | 6 +++--- 3 files changed, 10 insertions(+), 4 deletions(-) rename src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/{internal => tasks}/JavaJarExec.kt (73%) diff --git a/api/shadow.api b/api/shadow.api index c3c85ab58..eaf0e5f66 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -227,6 +227,12 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; } +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec : org/gradle/api/tasks/JavaExec { + public fun ()V + public fun exec ()V + public abstract fun getJarFile ()Lorg/gradle/api/file/RegularFileProperty; +} + public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask : org/gradle/api/DefaultTask { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Companion; public static final field DESC Ljava/lang/String; diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index d788a9b75..cdc290c45 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.internal.JavaJarExec import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText +import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import org.gradle.api.GradleException import org.gradle.api.Plugin diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt similarity index 73% rename from src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt index 3f62f20a3..7e74dc7cd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/JavaJarExec.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt @@ -1,13 +1,13 @@ -package com.github.jengelman.gradle.plugins.shadow.internal +package com.github.jengelman.gradle.plugins.shadow.tasks import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.InputFile import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.TaskAction -internal abstract class JavaJarExec : JavaExec() { +public abstract class JavaJarExec : JavaExec() { @get:InputFile - abstract val jarFile: RegularFileProperty + public abstract val jarFile: RegularFileProperty @TaskAction override fun exec() { From c1d57b9dc2fb9c4ebdb3c85108b9041c1fff83d5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 30 Nov 2024 23:14:41 -0500 Subject: [PATCH 060/941] Refine configurations in docs (#1071) * Defer configurations * Trim indents * Tweak styles * Rename testJar to testShadowJar --- src/docs/application-plugin/README.md | 4 +- src/docs/configuration/README.md | 16 +++--- src/docs/configuration/filtering/README.md | 8 +-- src/docs/configuration/merging/README.md | 55 ++++++++++++------- src/docs/configuration/minimizing/README.md | 6 +- src/docs/configuration/relocation/README.md | 24 ++++---- .../reproducible-builds/README.md | 4 +- src/docs/custom-tasks/README.md | 5 +- src/docs/getting-started/README.md | 28 +++++----- src/docs/plugins/README.md | 28 +++++----- src/docs/publishing/README.md | 2 +- 11 files changed, 95 insertions(+), 85 deletions(-) diff --git a/src/docs/application-plugin/README.md b/src/docs/application-plugin/README.md index e365fb198..60acd35c9 100644 --- a/src/docs/application-plugin/README.md +++ b/src/docs/application-plugin/README.md @@ -14,7 +14,7 @@ apply plugin: 'application' apply plugin: 'com.gradleup.shadow' application { - mainClass = 'myapp.Main' + mainClass = 'myapp.Main' } ``` @@ -28,7 +28,7 @@ It can be configured the same as any other `JavaExec` task. ```groovy // Configuring the runShadow Task -runShadow { +tasks.named('runShadow', com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec) { args 'foo' } ``` diff --git a/src/docs/configuration/README.md b/src/docs/configuration/README.md index 5ede2c3f1..8d2213526 100644 --- a/src/docs/configuration/README.md +++ b/src/docs/configuration/README.md @@ -25,9 +25,9 @@ As with all `Jar` tasks in Gradle, these values can be overridden: ```groovy // Output to build/libs/shadow.jar tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - archiveBaseName.set('shadow') - archiveClassifier.set('') - archiveVersion.set('') + archiveBaseName = 'shadow' + archiveClassifier = '' + archiveVersion = '' } ``` @@ -72,10 +72,10 @@ First, the manifest for the `shadowJar` task is configured to __inherit__ from t This means that any configuration performed on the `jar` task will propagate to the `shadowJar` tasks. ```groovy -jar { - manifest { - attributes 'Class-Path': '/libs/a.jar' - } +tasks.named('jar', Jar) { + manifest { + attributes 'Class-Path': '/libs/a.jar' + } } ``` @@ -89,7 +89,7 @@ If it is desired to inherit a manifest from a JAR task other than the standard ` on the `shadowJar.manifest` object can be used to configure the upstream. ```groovy -task testJar(type: Jar) { +tasks.register('testJar', Jar) { manifest { attributes 'Description': 'This is an application JAR' } diff --git a/src/docs/configuration/filtering/README.md b/src/docs/configuration/filtering/README.md index c77d66052..d7a2ee3bd 100644 --- a/src/docs/configuration/filtering/README.md +++ b/src/docs/configuration/filtering/README.md @@ -14,7 +14,7 @@ of the dependencies to be merged. ```groovy // Exclude a file from Shadow Jar tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - exclude 'a2.properties' + exclude 'a2.properties' } ``` @@ -25,8 +25,8 @@ Additionally, ANT style patterns can be used to match multiple files. ```groovy // Configuring output using ANT patterns tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - include '*.jar' - include '*.properties' - exclude 'a2.properties' + include '*.jar' + include '*.properties' + exclude 'a2.properties' } ``` diff --git a/src/docs/configuration/merging/README.md b/src/docs/configuration/merging/README.md index 72882fb65..2b6a6051c 100644 --- a/src/docs/configuration/merging/README.md +++ b/src/docs/configuration/merging/README.md @@ -14,15 +14,20 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.jetbrains.annotations.NotNull class MyTransformer implements Transformer { - boolean canTransformResource(FileTreeElement element) { true } + @Override + boolean canTransformResource(@NotNull FileTreeElement element) { return true } - void transform(TransformerContext context) {} + @Override + void transform(@NotNull TransformerContext context) {} - boolean hasTransformedResource() { true } + @Override + boolean hasTransformedResource() { return true } - void modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps) {} + @Override + void modifyOutputStream(@NotNull ZipOutputStream os, boolean preserveFileTimestamps) {} } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -38,18 +43,22 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.jetbrains.annotations.NotNull class MyTransformer implements Transformer { + boolean enabled - boolean enabled + @Override + boolean canTransformResource(@NotNull FileTreeElement element) { return true } - boolean canTransformResource(FileTreeElement element) { true } + @Override + void transform(@NotNull TransformerContext context) {} - void transform(TransformerContext context) {} + @Override + boolean hasTransformedResource() { return true } - boolean hasTransformedResource() { true } - - void modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps) {} + @Override + void modifyOutputStream(@NotNull ZipOutputStream os, boolean preserveFileTimestamps) {} } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -67,22 +76,30 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.jetbrains.annotations.NotNull class MyTransformer implements Transformer { + final boolean enabled - boolean enabled + MyTransformer(boolean enabled) { + this.enabled = enabled + } - boolean canTransformResource(FileTreeElement element) { true } + @Override + boolean canTransformResource(@NotNull FileTreeElement element) { return true } - void transform(TransformerContext context) {} + @Override + void transform(@NotNull TransformerContext context) {} - boolean hasTransformedResource() { true } + @Override + boolean hasTransformedResource() { return true } - void modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps) {} + @Override + void modifyOutputStream(@NotNull ZipOutputStream os, boolean preserveFileTimestamps) {} } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(new MyTransformer(enabled: true)) + transform(new MyTransformer(true)) } ``` @@ -186,11 +203,9 @@ It must be added using the [`transform`](https://gradleup.com/shadow/api/com/git ```groovy // Appending a XML File -import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(XmlAppendingTransformer.class) { + transform(com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer.class) { resource = 'properties.xml' } } -``` \ No newline at end of file +``` diff --git a/src/docs/configuration/minimizing/README.md b/src/docs/configuration/minimizing/README.md index 71e621664..b00f514f6 100644 --- a/src/docs/configuration/minimizing/README.md +++ b/src/docs/configuration/minimizing/README.md @@ -29,9 +29,9 @@ Similar to dependencies, projects can also be excluded. ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - minimize { - exclude(project(":api")) - } + minimize { + exclude(project(":api")) + } } ``` diff --git a/src/docs/configuration/relocation/README.md b/src/docs/configuration/relocation/README.md index f28ca7972..f94cf07c6 100644 --- a/src/docs/configuration/relocation/README.md +++ b/src/docs/configuration/relocation/README.md @@ -13,7 +13,7 @@ Any non-class files that are stored within a package structure are also relocate ```groovy // Relocating a Package tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - relocate 'junit.framework', 'shadow.junit' + relocate 'junit.framework', 'shadow.junit' } ``` @@ -39,12 +39,12 @@ syntax to specify matching path for your files and directories. ```groovy // Configuring Filtering for Relocation tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - relocate('junit.textui', 'a') { - exclude 'junit.textui.TestRunner' - } - relocate('junit.framework', 'b') { - include 'junit.framework.Test*' - } + relocate('junit.textui', 'a') { + exclude 'junit.textui.TestRunner' + } + relocate('junit.framework', 'b') { + include 'junit.framework.Test*' + } } ``` @@ -54,9 +54,9 @@ passing it to `include`/`exclude`. ```groovy // Configuring Filtering for Relocation with Regex tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - relocate('org.foo', 'a') { - include '%regex[org/foo/.*Factory[0-9].*]' - } + relocate('org.foo', 'a') { + include '%regex[org/foo/.*Factory[0-9].*]' + } } ``` @@ -72,8 +72,8 @@ To configure automatic dependency relocation, set `enableRelocation = true` and ```groovy // Configure Auto Relocation tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation = true - relocationPrefix = "myapp" + enableRelocation = true + relocationPrefix = "myapp" } ``` diff --git a/src/docs/configuration/reproducible-builds/README.md b/src/docs/configuration/reproducible-builds/README.md index e5595c168..79f7505e8 100644 --- a/src/docs/configuration/reproducible-builds/README.md +++ b/src/docs/configuration/reproducible-builds/README.md @@ -4,8 +4,8 @@ By default, JAR files generated by Gradle (with or without Shadow) for a single ```groovy tasks.withType(AbstractArchiveTask) { - preserveFileTimestamps = false - reproducibleFileOrder = true + preserveFileTimestamps = false + reproducibleFileOrder = true } ``` diff --git a/src/docs/custom-tasks/README.md b/src/docs/custom-tasks/README.md index 2fd693a4b..4c1575beb 100644 --- a/src/docs/custom-tasks/README.md +++ b/src/docs/custom-tasks/README.md @@ -7,8 +7,8 @@ dependencies to merge into the output. ```groovy // Shadowing Test Sources and Dependencies -task testJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - archiveClassifier.set("tests") +tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + archiveClassifier = "tests" from sourceSets.test.output configurations = [project.configurations.testRuntimeClasspath] } @@ -17,4 +17,3 @@ task testJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { The code snippet above will generate a shadowed JAR containing both the `main` and `test` sources as well as all `testRuntimeOnly` and `testImplementation` dependencies. The file is output to `build/libs/--tests.jar`. - diff --git a/src/docs/getting-started/README.md b/src/docs/getting-started/README.md index 8ad57fdcc..8963fe0aa 100644 --- a/src/docs/getting-started/README.md +++ b/src/docs/getting-started/README.md @@ -11,12 +11,12 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: ```groovy no-run buildscript { - repositories { - gradlePluginPortal() - } - dependencies { - classpath 'com.gradleup.shadow:shadow-gradle-plugin:@version@' - } + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'com.gradleup.shadow:shadow-gradle-plugin:@version@' + } } apply plugin: 'com.gradleup.shadow' @@ -29,15 +29,13 @@ apply plugin: 'java' ```groovy no-run buildscript { - repositories { - mavenCentral() - maven { - url 'https://oss.sonatype.org/content/repositories/snapshots/' - } - } - dependencies { - classpath 'com.gradleup.shadow:shadow-gradle-plugin:@snapshot-version@' - } + repositories { + mavenCentral() + maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } + } + dependencies { + classpath 'com.gradleup.shadow:shadow-gradle-plugin:@snapshot-version@' + } } apply plugin: 'com.gradleup.shadow' diff --git a/src/docs/plugins/README.md b/src/docs/plugins/README.md index 797e0a2c8..2ea99dd7c 100644 --- a/src/docs/plugins/README.md +++ b/src/docs/plugins/README.md @@ -10,26 +10,24 @@ and `relocationPrefix` settings on any `ShadowJar` task. A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the `shadowJar` task for relocation. -```groovy no-run -plugins { - id 'com.gradleup.shadow' version '@version@' - id 'java' -} +```groovy +apply plugin: 'java' +apply plugin: 'com.gradleup.shadow' dependencies { - shadow localGroovy() - shadow gradleApi() - - implementation 'org.jdom:jdom2:2.0.6' - implementation 'org.ow2.asm:asm:6.0' - implementation 'org.ow2.asm:asm-commons:6.0' - implementation 'commons-io:commons-io:2.4' - implementation 'org.apache.ant:ant:1.9.4' - implementation 'org.codehaus.plexus:plexus-utils:2.0.6' + shadow localGroovy() + shadow gradleApi() + + implementation 'org.jdom:jdom2:2.0.6' + implementation 'org.ow2.asm:asm:6.0' + implementation 'org.ow2.asm:asm-commons:6.0' + implementation 'commons-io:commons-io:2.4' + implementation 'org.apache.ant:ant:1.9.4' + implementation 'org.codehaus.plexus:plexus-utils:2.0.6' } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation = true + enableRelocation = true } ``` diff --git a/src/docs/publishing/README.md b/src/docs/publishing/README.md index a59d768c9..596fe3eeb 100644 --- a/src/docs/publishing/README.md +++ b/src/docs/publishing/README.md @@ -21,7 +21,7 @@ publishing { } repositories { maven { - url "http://repo.myorg.com" + url "https://repo.myorg.com" } } } From 9414a504e734b271190eb4dc562f24a157f4d827 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 1 Dec 2024 02:47:49 -0500 Subject: [PATCH 061/941] Mark some properties Optional in ShadowJar (#1074) * Note testShadowJar properties * Mark enableRelocation, relocationPrefix, and minimizeJar Optional * Add a regression test * Note this change * Tweak test and doc --- src/docs/changes/README.md | 5 +++ src/docs/custom-tasks/README.md | 13 +++++++- .../plugins/shadow/ShadowPluginSpec.groovy | 30 +++++++++++++++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 33 +++++++++++++++---- 4 files changed, 73 insertions(+), 8 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index a54a5d83c..5606e3a48 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,11 @@ ## [Unreleased] +**Fixed** + +- Mark `enableRelocation`, `relocationPrefix`, and `minimizeJar` optional. ([#1074](https://github.com/GradleUp/shadow/pull/1074)) + This fix the regression for registering custom `ShadowJar` tasks. + ## [v9.0.0-beta2] (2024-11-28) diff --git a/src/docs/custom-tasks/README.md b/src/docs/custom-tasks/README.md index 4c1575beb..f94890bcb 100644 --- a/src/docs/custom-tasks/README.md +++ b/src/docs/custom-tasks/README.md @@ -7,10 +7,21 @@ dependencies to merge into the output. ```groovy // Shadowing Test Sources and Dependencies -tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { +def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + description = "Create a combined JAR of project and test dependencies" + archiveClassifier = "tests" from sourceSets.test.output configurations = [project.configurations.testRuntimeClasspath] + includedDependencies.setFrom(configurations) + + // May have to configure more properties here, you can ref https://github.com/GradleUp/shadow/blob/8ddb7c5334d15ef054f37aa9999e7680a55c5b2a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt#L94-L99 for more details. +} + +// Optionally, make the `assemble` task depend on the new task +tasks.named('assemble') { + dependsOn testShadowJar } ``` diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index 742229ef3..3db47c414 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -8,6 +8,7 @@ import org.gradle.api.artifacts.Configuration import org.gradle.api.plugins.JavaPlugin import org.gradle.testfixtures.ProjectBuilder import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.TaskOutcome import spock.lang.Ignore import spock.lang.IgnoreIf import spock.lang.Issue @@ -1244,6 +1245,35 @@ class ShadowPluginSpec extends PluginSpecification { assert jar.entries().collect().findAll { it.name.endsWith('.class') }.size() == 1 } + @Issue("https://github.com/GradleUp/shadow/issues/1070") + def 'can register a custom shadow jar task'() { + buildFile << """ + dependencies { + testImplementation 'junit:junit:3.8.2' + } + + def testShadowJar = tasks.register('testShadowJar', ${ShadowJar.name}) { + group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + description = "Create a combined JAR of project and test dependencies" + + archiveClassifier = "tests" + from sourceSets.test.output + configurations = [project.configurations.testRuntimeClasspath] + includedDependencies.setFrom(configurations) + } + """.stripIndent() + + when: + def result = run('testShadowJar') + + then: + assert result.task(":testShadowJar").outcome == TaskOutcome.SUCCESS + + and: + def jarFile = new JarFile(output("shadow-1.0-tests.jar")) + assert jarFile.getEntry('junit') != null + } + private String escapedPath(File file) { file.path.replaceAll('\\\\', '\\\\\\\\') } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index f0a1ab085..b50b52392 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.tasks +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.DefaultZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter @@ -67,7 +68,7 @@ public abstract class ShadowJar : @get:Classpath public val toMinimize: ConfigurableFileCollection by unsafeLazy { objectFactory.fileCollection().apply { - if (minimizeJar.get()) { + if (minimizeJar.getOrElse(false)) { setFrom(dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) } } @@ -76,7 +77,7 @@ public abstract class ShadowJar : @get:Classpath public val apiJars: ConfigurableFileCollection by unsafeLazy { objectFactory.fileCollection().apply { - if (minimizeJar.get()) { + if (minimizeJar.getOrElse(false)) { setFrom(UnusedTracker.getApiJarsFromProject(project)) } } @@ -86,7 +87,7 @@ public abstract class ShadowJar : @get:PathSensitive(PathSensitivity.RELATIVE) public val sourceSetsClassesDirs: ConfigurableFileCollection by unsafeLazy { objectFactory.fileCollection().apply { - if (minimizeJar.get()) { + if (minimizeJar.getOrElse(false)) { project.extensions.getByType(SourceSetContainer::class.java).forEach { sourceSet -> from(sourceSet.output.classesDirs.filter { it.isDirectory }) } @@ -124,13 +125,31 @@ public abstract class ShadowJar : @get:Internal public abstract val dependencyFilter: Property + /** + * Enable relocation of packages in the jar. + * + * Defaults to false. + */ @get:Input + @get:Optional public abstract val enableRelocation: Property + /** + * Prefix to use for relocated packages. + * + * Defaults to [ShadowBasePlugin.SHADOW]. + */ @get:Input + @get:Optional public abstract val relocationPrefix: Property + /** + * Minimize the jar by removing unused classes. + * + * Defaults to false. + */ @get:Input + @get:Optional public abstract val minimizeJar: Property @Internal @@ -239,7 +258,7 @@ public abstract class ShadowJar : override fun createCopyAction(): CopyAction { val documentationRegistry = services.get(DocumentationRegistry::class.java) - val unusedTracker = if (minimizeJar.get()) { + val unusedTracker = if (minimizeJar.getOrElse(false)) { UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) } else { null @@ -254,7 +273,7 @@ public abstract class ShadowJar : rootPatternSet, stats, isPreserveFileTimestamps, - minimizeJar.get(), + minimizeJar.getOrElse(false), unusedTracker, ) } @@ -279,9 +298,9 @@ public abstract class ShadowJar : private val packageRelocators: List get() { - if (!enableRelocation.get()) return emptyList() + if (!enableRelocation.getOrElse(false)) return emptyList() - val prefix = relocationPrefix.get() + val prefix = relocationPrefix.getOrElse(ShadowBasePlugin.SHADOW) // Must cast configurations to List to fix type mismatch in runtime. return (configurations.get() as List).flatMap { configuration -> configuration.files.flatMap { file -> From e6b7e7b6f873e38fa3a7fddf17ab923110619d6d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 1 Dec 2024 10:07:10 -0500 Subject: [PATCH 062/941] Overload RelocateClassContext and RelocatePathContext (#1075) * Overload RelocateClassContext and RelocatePathContext * Simplify SimpleRelocatorTest * Add extensions for Relocator * Revert "Add extensions for Relocator" This reverts commit 193d93101c3cf06dd646682dfe3ec39960f34114. --- api/shadow.api | 4 ++++ .../shadow/relocation/RelocateClassContext.kt | 4 ++-- .../shadow/relocation/RelocatePathContext.kt | 4 ++-- .../shadow/relocation/SimpleRelocatorTest.kt | 22 +++++++++---------- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index eaf0e5f66..0b2dea4c4 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -117,7 +117,9 @@ public abstract interface annotation class com/github/jengelman/gradle/plugins/s public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Companion; + public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public synthetic fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder; public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; @@ -143,7 +145,9 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Companion; + public fun (Ljava/lang/String;)V public fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public synthetic fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder; public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt index 72f8c8623..b30c1c31d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt @@ -2,9 +2,9 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.ShadowStats -public data class RelocateClassContext( +public data class RelocateClassContext @JvmOverloads constructor( val className: String, - val stats: ShadowStats, + val stats: ShadowStats = ShadowStats(), ) { public class Builder { private var className = "" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt index 63e4655cc..ff8ca3288 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt @@ -2,9 +2,9 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.ShadowStats -public data class RelocatePathContext( +public data class RelocatePathContext @JvmOverloads constructor( val path: String, - val stats: ShadowStats, + val stats: ShadowStats = ShadowStats(), ) { public class Builder { private var path = "" diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index ad8d042b9..bf7c6cf3d 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -142,48 +142,48 @@ class SimpleRelocatorTest { @Test fun testCanRelocateAbsClassPath() { val relocator = SimpleRelocator("org.apache.velocity", "org.apache.momentum") - assertThat(relocator.relocatePath(pathContext("/org/apache/velocity/mass.properties"))) + assertThat(relocator.relocatePath("/org/apache/velocity/mass.properties")) .isEqualTo("/org/apache/momentum/mass.properties") } @Test fun testRelocatePath() { var relocator = SimpleRelocator("org.foo") - assertThat(relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) + assertThat(relocator.relocatePath("org/foo/bar/Class.class")) .isEqualTo("hidden/org/foo/bar/Class.class") relocator = SimpleRelocator("org.foo", "private.stuff") - assertThat(relocator.relocatePath(pathContext("org/foo/bar/Class.class"))) + assertThat(relocator.relocatePath("org/foo/bar/Class.class")) .isEqualTo("private/stuff/bar/Class.class") } @Test fun testRelocateClass() { var relocator = SimpleRelocator("org.foo") - assertThat(relocator.relocateClass(classContext())) + assertThat(relocator.relocateClass("org.foo.bar.Class")) .isEqualTo("hidden.org.foo.bar.Class") relocator = SimpleRelocator("org.foo", "private.stuff") - assertThat(relocator.relocateClass(classContext())) + assertThat(relocator.relocateClass("org.foo.bar.Class")) .isEqualTo("private.stuff.bar.Class") } @Test fun testRelocateRawString() { var relocator = SimpleRelocator("Lorg/foo", "Lhidden/org/foo", _rawString = true) - assertThat(relocator.relocatePath(pathContext("(I)Lorg/foo/bar/Class"))) + assertThat(relocator.relocatePath("(I)Lorg/foo/bar/Class")) .isEqualTo("(I)Lhidden/org/foo/bar/Class") relocator = SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", _rawString = true) - assertThat(relocator.relocatePath(pathContext("META-INF/org.foo.xml"))) + assertThat(relocator.relocatePath("META-INF/org.foo.xml")) .isEqualTo("META-INF/hidden.org.foo.xml") } - private fun pathContext(path: String): RelocatePathContext { - return RelocatePathContext.builder().path(path).build() + private fun SimpleRelocator.relocatePath(path: String): String { + return relocatePath(RelocatePathContext(path)) } - private fun classContext(className: String = "org.foo.bar.Class"): RelocateClassContext { - return RelocateClassContext.builder().className(className).build() + private fun SimpleRelocator.relocateClass(className: String): String { + return relocateClass(RelocateClassContext(className)) } } From b7c4c089c5e4a554c3437a783cc9e37ae5c69666 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 2 Dec 2024 09:46:28 +0800 Subject: [PATCH 063/941] Update more kdoc links --- .../jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt | 3 ++- .../jengelman/gradle/plugins/shadow/relocation/Relocator.kt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt index 395fa5bb3..92ddede28 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt @@ -9,7 +9,8 @@ import java.util.regex.Pattern import org.objectweb.asm.commons.Remapper /** - * Modified from `org.apache.maven.plugins.shade.DefaultShader.java#RelocatorRemapper` + * Modified from [org.apache.maven.plugins.shade.DefaultShader.RelocatorRemapper](https://github.com/apache/maven-shade-plugin/blob/83c123d1f9c5f6927af2aca12ee322b5168a7c63/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java#L689-L772). + * Modified from [org.apache.maven.plugins.shade.DefaultShader.DefaultPackageMapper](https://github.com/apache/maven-shade-plugin/blob/199ffaecd26a912527173ed4edae366e48a00998/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java#L737-L774). * * @author John Engelman */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt index 486877e7b..11cda6395 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation /** - * Modified from `org.apache.maven.plugins.shade.relocation.Relocator.java` + * Modified from [org.apache.maven.plugins.shade.relocation.Relocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java). * * @author Jason van Zyl * @author John Engelman From 5e7ac71bf6a40151236b7ee041268f38472bd226 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 2 Dec 2024 11:06:20 +0800 Subject: [PATCH 064/941] Ignore .kotlin --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 646e97241..03ace8838 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ src/docs/.vuepress/dist/ jd-gui.cfg bin/ .vscode/ +.kotlin From 05604ae6b0d7a868fca991be08d38866c579884c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 3 Dec 2024 08:31:43 +0800 Subject: [PATCH 065/941] Update plugin android-lint to v8.7.3 (#1080) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 406bfe1d4..4228ce9d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,6 +23,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin = "org.jetbrains.kotlin.jvm:2.1.0" -android-lint = "com.android.lint:8.7.2" +android-lint = "com.android.lint:8.7.3" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.16.3" spotless = "com.diffplug.spotless:7.0.0.BETA4" From 6270b4568210ba1df8ef7204bbc1d9cf177d64bc Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 3 Dec 2024 09:19:30 +0800 Subject: [PATCH 066/941] Share testObjectFactory --- .../transformers/ApacheNoticeResourceTransformerTest.kt | 3 ++- .../plugins/shadow/transformers/AppendingTransformerTest.kt | 3 ++- .../shadow/transformers/ManifestAppenderTransformerTest.kt | 3 ++- .../shadow/transformers/PropertiesFileTransformerTest.kt | 3 ++- .../plugins/shadow/transformers/TransformerTestSupport.kt | 2 -- .../shadow/transformers/XmlAppendingTransformerTest.kt | 3 ++- .../github/jengelman/gradle/plugins/shadow/util/Utils.kt | 6 ++++++ 7 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt index b7da3fbb9..05d013dbf 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -4,6 +4,7 @@ import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue import assertk.fail +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -18,7 +19,7 @@ class ApacheNoticeResourceTransformerTest : TransformerTestSupport() @BeforeEach fun setup() { - transformer = AppendingTransformer(objectFactory) + transformer = AppendingTransformer(testObjectFactory) } @Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt index 4c5c21714..9c1e8c084 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -6,13 +6,14 @@ import assertk.assertions.isFalse import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class ManifestAppenderTransformerTest : TransformerTestSupport() { @BeforeEach fun setup() { - transformer = ManifestAppenderTransformer(objectFactory) + transformer = ManifestAppenderTransformer(testObjectFactory) } @Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index 0b8af0815..3106cc9ba 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -6,6 +6,7 @@ import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isNotEmpty import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -13,7 +14,7 @@ class PropertiesFileTransformerTest : TransformerTestSupport { protected lateinit var transformer: T @@ -24,7 +23,6 @@ abstract class TransformerTestSupport { protected companion object { const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" - val objectFactory = ProjectBuilder.builder().build().objects fun getFileElement(path: String): FileTreeElement { return createDefaultFileTreeElement(relativePath = RelativePath.parse(true, path)) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index 5bb4b85f4..1eaf76436 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -14,7 +15,7 @@ class XmlAppendingTransformerTest : TransformerTestSupport Date: Tue, 3 Dec 2024 09:35:35 +0800 Subject: [PATCH 067/941] Update release task info --- build.gradle.kts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 58b4ff7d4..aa4265751 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -141,6 +141,9 @@ tasks.whenTaskAdded { } tasks.register("release") { + description = "Publishes the plugin to maven repos and deploys website." + group = LifecycleBasePlugin.VERIFICATION_GROUP + dependsOn( tasks.publish, tasks.publishPlugins, From 6c1920568d4c9c98ed5bb86ebf106a92ff63ef01 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 2 Dec 2024 21:26:19 -0500 Subject: [PATCH 068/941] Add a new source set to share test kits (#1081) * Share Utils.kt * Rename testFixtures to testKit * Depend on the output of main --- build.gradle.kts | 10 +++++++++- .../PropertiesFileTransformerSpec.groovy | 13 +++++++------ .../transformers/TransformerSpecSupport.groovy | 3 --- .../ApacheNoticeResourceTransformerTest.kt | 2 +- .../shadow/transformers/AppendingTransformerTest.kt | 2 +- .../transformers/ManifestAppenderTransformerTest.kt | 2 +- .../transformers/PropertiesFileTransformerTest.kt | 2 +- .../transformers/XmlAppendingTransformerTest.kt | 2 +- .../gradle/plugins/shadow/testkit}/util/Utils.kt | 4 +++- 9 files changed, 24 insertions(+), 16 deletions(-) rename src/{test/kotlin/com/github/jengelman/gradle/plugins/shadow => testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit}/util/Utils.kt (65%) diff --git a/build.gradle.kts b/build.gradle.kts index aa4265751..bf84d0158 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,6 +43,9 @@ spotless { } } +val testKit: SourceSet by sourceSets.creating +val testKitImplementation: Configuration by configurations.getting + val intiTest: SourceSet by sourceSets.creating val intiTestImplementation: Configuration by configurations.getting { extendsFrom(configurations.testImplementation.get()) @@ -74,6 +77,11 @@ dependencies { implementation(libs.plexus.utils) implementation(libs.plexus.xml) + val mainOutput = sourceSets.main.map { it.output } + testKitImplementation(mainOutput) + testKitImplementation(gradleTestKit()) + + testImplementation(testKit.output) testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) testImplementation(libs.assertk) @@ -85,7 +93,7 @@ dependencies { exclude(group = "org.codehaus.groovy") exclude(group = "org.hamcrest") } - funcTestImplementation(sourceSets.main.get().output) + funcTestImplementation(mainOutput) lintChecks(libs.androidx.gradlePluginLints) lintChecks(libs.assertk.lint) diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy index 72a5e40c3..6fc067a6b 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy @@ -22,6 +22,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import spock.lang.Unroll +import static com.github.jengelman.gradle.plugins.shadow.testkit.util.Utils.testObjectFactory import static groovy.lang.Closure.IDENTITY @Unroll @@ -29,7 +30,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void "Path #path #transform transformed"() { given: - Transformer transformer = new PropertiesFileTransformer(objectFactory) + Transformer transformer = new PropertiesFileTransformer(testObjectFactory) when: boolean actual = transformer.canTransformResource(getFileElement(path)) @@ -49,7 +50,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void exerciseAllTransformConfigurations() { given: def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) + Transformer transformer = new PropertiesFileTransformer(testObjectFactory) transformer.mergeStrategy.set(MergeStrategy.from(mergeStrategy)) transformer.mergeSeparator.set(mergeSeparator) @@ -73,7 +74,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void exerciseAllTransformConfigurationsWithPaths() { given: def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) + Transformer transformer = new PropertiesFileTransformer(testObjectFactory) transformer.paths.set(paths) transformer.mergeStrategy.set(MergeStrategy.from('first')) @@ -97,7 +98,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void exerciseAllTransformConfigurationsWithMappings() { given: def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) + Transformer transformer = new PropertiesFileTransformer(testObjectFactory) transformer.mappings.set(mappings) transformer.mergeStrategy.set(MergeStrategy.from('latest')) @@ -123,7 +124,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void appliesKeyTransformer() { given: def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) + Transformer transformer = new PropertiesFileTransformer(testObjectFactory) transformer.keyTransformer.set(keyTransformer) transformer.mergeStrategy.set(MergeStrategy.from('append')) @@ -147,7 +148,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void appliesCharset() { given: def element = getFileElement(path) - def transformer = new PropertiesFileTransformer(objectFactory) + def transformer = new PropertiesFileTransformer(testObjectFactory) transformer.charsetName.set(charset) when: diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy index 818697e0b..86978937c 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy @@ -4,14 +4,11 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowStats import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath import org.gradle.api.internal.file.DefaultFileTreeElement -import org.gradle.testfixtures.ProjectBuilder import spock.lang.Shared import spock.lang.Specification class TransformerSpecSupport extends Specification { - protected static final def objectFactory = ProjectBuilder.builder().build().objects - @Shared ShadowStats stats diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt index 05d013dbf..f8cf651ff 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -4,7 +4,7 @@ import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue import assertk.fail -import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index b5eee7d89..681000705 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -3,7 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt index 9c1e8c084..935c98da6 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -6,7 +6,7 @@ import assertk.assertions.isFalse import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index 3106cc9ba..81e084d43 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -6,7 +6,7 @@ import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isNotEmpty import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index 1eaf76436..32895d9fc 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -3,7 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/util/Utils.kt similarity index 65% rename from src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt rename to src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/util/Utils.kt index a6acfd481..0785681b0 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/util/Utils.kt @@ -1,4 +1,6 @@ -package com.github.jengelman.gradle.plugins.shadow.util +@file:JvmName("Utils") + +package com.github.jengelman.gradle.plugins.shadow.testkit.util import org.gradle.api.model.ObjectFactory import org.gradle.testfixtures.ProjectBuilder From 5c090c17bb3d4cd5ba2a95b559641e5f07707edd Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 3 Dec 2024 03:22:13 -0500 Subject: [PATCH 069/941] Eliminate redundant TestFile in functional tests (#1083) * Remove TestFile and related classes * Add FileExtensions * Rename file to resolve --- .../plugins/shadow/PublishingSpec.groovy | 24 +- .../util/AppendableMavenFileRepository.groovy | 2 +- .../plugins/shadow/util/FileExtensions.groovy | 26 + .../shadow/util/PluginSpecification.groovy | 3 +- .../shadow/util/file/ExecOutput.groovy | 13 - .../plugins/shadow/util/file/TestFile.java | 536 ------------------ .../shadow/util/file/TestFileHelper.groovy | 203 ------- .../util/file/TestWorkspaceBuilder.groovy | 38 -- .../shadow/util/repo/AbstractModule.groovy | 29 +- .../repo/maven/AbstractMavenModule.groovy | 27 +- .../util/repo/maven/MavenFileModule.groovy | 5 +- .../repo/maven/MavenFileRepository.groovy | 7 +- .../shadow/util/repo/maven/MavenModule.groovy | 7 +- ...rg.codehaus.groovy.runtime.ExtensionModule | 3 + 14 files changed, 78 insertions(+), 845 deletions(-) create mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/ExecOutput.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy create mode 100644 src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy index 8787c532b..ce60a5e12 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy @@ -60,14 +60,14 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File publishedFile = publishingRepo.rootDir.file('shadow/maven-all/1.0/maven-all-1.0.jar').canonicalFile + File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').canonicalFile assert publishedFile.exists() and: contains(publishedFile, ['a.properties', 'a2.properties']) and: - File pom = publishingRepo.rootDir.file('shadow/maven-all/1.0/maven-all-1.0.pom').canonicalFile + File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').canonicalFile assert pom.exists() def contents = new XmlSlurper().parse(pom) @@ -128,7 +128,7 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File publishedFile = publishingRepo.rootDir.file('shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext').canonicalFile + File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext').canonicalFile assert publishedFile.exists() } @@ -209,14 +209,14 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File publishedFile = publishingRepo.rootDir.file('shadow/maven-all/1.0/maven-all-1.0.jar').canonicalFile + File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').canonicalFile assert publishedFile.exists() and: contains(publishedFile, ['a.properties', 'a2.properties']) and: - File pom = publishingRepo.rootDir.file('shadow/maven-all/1.0/maven-all-1.0.pom').canonicalFile + File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').canonicalFile assert pom.exists() def contents = new XmlSlurper().parse(pom) @@ -273,8 +273,8 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File mainJar = publishingRepo.rootDir.file('com/acme/maven/1.0/maven-1.0.jar').canonicalFile - File shadowJar = publishingRepo.rootDir.file('com/acme/maven/1.0/maven-1.0-all.jar').canonicalFile + File mainJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.jar').canonicalFile + File shadowJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0-all.jar').canonicalFile assert mainJar.exists() assert shadowJar.exists() @@ -282,8 +282,8 @@ class PublishingSpec extends PluginSpecification { contains(shadowJar, ['a.properties', 'a2.properties']) and: "publishes both a POM file and a Gradle metadata file" - File pom = publishingRepo.rootDir.file('com/acme/maven/1.0/maven-1.0.pom').canonicalFile - File gmm = publishingRepo.rootDir.file('com/acme/maven/1.0/maven-1.0.module').canonicalFile + File pom = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.pom').canonicalFile + File gmm = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.module').canonicalFile pom.exists() gmm.exists() @@ -328,13 +328,13 @@ class PublishingSpec extends PluginSpecification { and: "verify shadow publication" assertions { - shadowJar = publishingRepo.rootDir.file('com/acme/maven-all/1.0/maven-all-1.0-all.jar').canonicalFile + shadowJar = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0-all.jar').canonicalFile assert shadowJar.exists() contains(shadowJar, ['a.properties', 'a2.properties']) } assertions { - pom = publishingRepo.rootDir.file('com/acme/maven-all/1.0/maven-all-1.0.pom').canonicalFile + pom = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.pom').canonicalFile assert pom.exists() pomContents = new XmlSlurper().parse(pom) assert pomContents.dependencies[0].dependency.size() == 1 @@ -351,7 +351,7 @@ class PublishingSpec extends PluginSpecification { } assertions { - gmm = publishingRepo.rootDir.file('com/acme/maven-all/1.0/maven-all-1.0.module').canonicalFile + gmm = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.module').canonicalFile assert gmm.exists() gmmContents = new JsonSlurper().parse(gmm) assert gmmContents.variants.size() == 1 diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy index c92314796..cc79dc0b6 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy @@ -8,7 +8,7 @@ class AppendableMavenFileRepository extends MavenFileRepository { @Override AppendableMavenFileModule module(String groupId, String artifactId, Object version = '1.0') { - def artifactDir = rootDir.file("${groupId.replace('.', '/')}/$artifactId/$version") + def artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") return new AppendableMavenFileModule(artifactDir, groupId, artifactId, version as String) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy new file mode 100644 index 000000000..936c0bbf9 --- /dev/null +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy @@ -0,0 +1,26 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +/** + * TODO: this is used as extensions for Groovy, could be replaced after migrated to Kotlin. + * Registered in resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule. + */ +final class FileExtensions { + static final File resolve(File file, String relativePath) { + try { + return new File(file, relativePath) + } catch (RuntimeException e) { + throw new RuntimeException(String.format("Could not locate file '%s' relative to '%s'.", Arrays.toString(relativePath), file), e) + } + } + + static final File createDir(File file) { + if (file.mkdirs()) { + return file + } + if (file.isDirectory()) { + return file + } + throw new AssertionError("Problems creating dir: " + file + + ". Diagnostics: exists=" + file.exists() + ", isFile=" + file.isFile() + ", isDirectory=" + file.isDirectory()) + } +} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy index 96f2c459a..23b88c502 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.util -import com.github.jengelman.gradle.plugins.shadow.util.file.TestFile import org.codehaus.plexus.util.IOUtil import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner @@ -121,7 +120,7 @@ abstract class PluginSpecification extends Specification { } AppendableMavenFileRepository repo(String path = 'maven-repo') { - new AppendableMavenFileRepository(new TestFile(dir.toFile(), path)) + new AppendableMavenFileRepository(dir.resolve(path).toFile()) } void assertJarFileContentsEqual(File f, String path, String contents) { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/ExecOutput.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/ExecOutput.groovy deleted file mode 100644 index 3bd0f93f0..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/ExecOutput.groovy +++ /dev/null @@ -1,13 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.file - -class ExecOutput { - ExecOutput(String rawOutput, String error) { - this.rawOutput = rawOutput - this.out = rawOutput.replaceAll("\r\n|\r", "\n") - this.error = error - } - - String rawOutput - String out - String error -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java deleted file mode 100644 index da2976a59..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFile.java +++ /dev/null @@ -1,536 +0,0 @@ -/* - * Copyright 2010 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.util.file; - -import groovy.lang.Closure; -import org.apache.commons.io.FileUtils; -import org.apache.tools.ant.Project; -import org.apache.tools.ant.Task; -import org.apache.tools.ant.taskdefs.Tar; -import org.apache.tools.ant.taskdefs.Zip; -import org.apache.tools.ant.types.EnumeratedAttribute; -import org.codehaus.groovy.runtime.ResourceGroovyMethods; - -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.charset.Charset; -import java.security.MessageDigest; -import java.util.*; -import java.util.jar.JarFile; -import java.util.jar.Manifest; - -import static org.junit.jupiter.api.Assertions.*; - -public class TestFile extends File { - private boolean useNativeTools; - - public TestFile(File file, Object... path) { - super(join(file, path).getAbsolutePath()); - } - - public TestFile(URI uri) { - this(new File(uri)); - } - - public TestFile(String path) { - this(new File(path)); - } - - public TestFile(URL url) { - this(toUri(url)); - } - - public TestFile usingNativeTools() { - useNativeTools = true; - return this; - } - - Object writeReplace() throws ObjectStreamException { - return new File(getAbsolutePath()); - } - - @Override - public File getCanonicalFile() throws IOException { - return new File(getAbsolutePath()).getCanonicalFile(); - } - - @Override - public String getCanonicalPath() throws IOException { - return new File(getAbsolutePath()).getCanonicalPath(); - } - - private static URI toUri(URL url) { - try { - return url.toURI(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - } - - private static File join(File file, Object[] path) { - File current = file.getAbsoluteFile(); - for (Object p : path) { - current = new File(current, p.toString()); - } - try { - return current.getCanonicalFile(); - } catch (IOException e) { - throw new RuntimeException(String.format("Could not canonicalise '%s'.", current), e); - } - } - - public TestFile file(Object... path) { - try { - return new TestFile(this, path); - } catch (RuntimeException e) { - throw new RuntimeException(String.format("Could not locate file '%s' relative to '%s'.", Arrays.toString(path), this), e); - } - } - - public List files(Object... paths) { - List files = new ArrayList<>(); - for (Object path : paths) { - files.add(file(path)); - } - return files; - } - - public TestFile withExtension(String extension) { - return getParentFile().file(getName().replaceAll("\\..*$", "." + extension)); - } - - public TestFile writelns(String... lines) { - return writelns(Arrays.asList(lines)); - } - - public TestFile write(Object content) { - try { - FileUtils.writeStringToFile(this, content.toString(), Charset.defaultCharset()); - } catch (IOException e) { - throw new RuntimeException(String.format("Could not write to test file '%s'", this), e); - } - return this; - } - - public TestFile leftShift(Object content) { - getParentFile().mkdirs(); - try { - ResourceGroovyMethods.leftShift(this, content); - return this; - } catch (IOException e) { - throw new RuntimeException(String.format("Could not append to test file '%s'", this), e); - } - } - - @Override - public TestFile[] listFiles() { - File[] children = super.listFiles(); - TestFile[] files = new TestFile[children.length]; - for (int i = 0; i < children.length; i++) { - File child = children[i]; - files[i] = new TestFile(child); - } - return files; - } - - public String getText() { - assertIsFile(); - try { - return FileUtils.readFileToString(this, Charset.defaultCharset()); - } catch (IOException e) { - throw new RuntimeException(String.format("Could not read from test file '%s'", this), e); - } - } - - public Map getProperties() { - assertIsFile(); - Properties properties = new Properties(); - try { - try (FileInputStream inStream = new FileInputStream(this)) { - properties.load(inStream); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - Map map = new HashMap<>(); - for (Object key : properties.keySet()) { - map.put(key.toString(), properties.getProperty(key.toString())); - } - return map; - } - - public Manifest getManifest() { - assertIsFile(); - try { - try (JarFile jarFile = new JarFile(this)) { - return jarFile.getManifest(); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void unzipTo(File target) { - assertIsFile(); - new TestFileHelper(this).unzipTo(target, useNativeTools); - } - - public void untarTo(File target) { - assertIsFile(); - - new TestFileHelper(this).untarTo(target, useNativeTools); - } - - public void copyTo(File target) { - if (isDirectory()) { - try { - FileUtils.copyDirectory(this, target); - } catch (IOException e) { - throw new RuntimeException(String.format("Could not copy test directory '%s' to '%s'", this, - target), e); - } - } else { - try { - FileUtils.copyFile(this, target); - } catch (IOException e) { - throw new RuntimeException(String.format("Could not copy test file '%s' to '%s'", this, target), e); - } - } - } - - public void copyFrom(File target) { - new TestFile(target).copyTo(this); - } - - public void copyFrom(URL resource) { - try { - FileUtils.copyURLToFile(resource, this); - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public void moveToDirectory(File target) { - if (target.exists() && !target.isDirectory()) { - throw new RuntimeException(String.format("Target '%s' is not a directory", target)); - } - try { - FileUtils.moveFileToDirectory(this, target, true); - } catch (IOException e) { - throw new RuntimeException(String.format("Could not move test file '%s' to directory '%s'", this, target), e); - } - } - - public TestFile touch() { - try { - FileUtils.touch(this); - } catch (IOException e) { - throw new RuntimeException(e); - } - assertIsFile(); - return this; - } - - /** - * Creates a directory structure specified by the given closure. - *
-     * dir.create {
-     *     subdir1 {
-     *        file 'somefile.txt'
-     *     }
-     *     subdir2 { nested { file 'someFile' } }
-     * }
-     * 
- */ - public TestFile create(Closure structure) { - assertTrue(isDirectory() || mkdirs()); - new TestWorkspaceBuilder(this).apply(structure); - return this; - } - - @Override - public TestFile getParentFile() { - return super.getParentFile() == null ? null : new TestFile(super.getParentFile()); - } - - public TestFile writelns(Iterable lines) { - Formatter formatter = new Formatter(); - for (String line : lines) { - formatter.format("%s%n", line); - } - return write(formatter); - } - - public TestFile assertExists() { - assertTrue(exists(), String.format("%s does not exist", this)); - return this; - } - - public TestFile assertIsFile() { - assertTrue(isFile(), String.format("%s is not a file", this)); - return this; - } - - public TestFile assertIsDir() { - assertTrue(isDirectory(), String.format("%s is not a directory", this)); - return this; - } - - public TestFile assertDoesNotExist() { - assertFalse(exists(), String.format("%s should not exist", this)); - return this; - } - - public TestFile assertIsCopyOf(TestFile other) { - assertIsFile(); - other.assertIsFile(); - assertEquals(other.length(), this.length(), String.format("%s is not the same length as %s", this, other)); - assertArrayEquals(getHash("MD5"), other.getHash("MD5"), String.format("%s does not have the same content as %s", this, other)); - return this; - } - - private byte[] getHash(String algorithm) { - try { - MessageDigest messageDigest = MessageDigest.getInstance(algorithm); - messageDigest.update(FileUtils.readFileToByteArray(this)); - return messageDigest.digest(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - public String readLink() { - assertExists(); - return new TestFileHelper(this).readLink(); - } - - public String getPermissions() { - assertExists(); - return new TestFileHelper(this).getPermissions(); - } - - public TestFile setPermissions(String permissions) { - assertExists(); - new TestFileHelper(this).setPermissions(permissions); - return this; - } - - public TestFile setMode(int mode) { - assertExists(); - new TestFileHelper(this).setMode(mode); - return this; - } - - public int getMode() { - assertExists(); - return new TestFileHelper(this).getMode(); - } - - /** - * Asserts that this file contains exactly the given set of descendants. - */ - public TestFile assertHasDescendants(String... descendants) { - Set actual = new TreeSet<>(); - assertIsDir(); - visit(actual, "", this); - Set expected = new TreeSet<>(Arrays.asList(descendants)); - - Set extras = new TreeSet<>(actual); - extras.removeAll(expected); - Set missing = new TreeSet<>(expected); - missing.removeAll(actual); - - assertEquals(expected, actual, String.format("For dir: %s, extra files: %s, missing files: %s, expected: %s", this, extras, missing, expected)); - - return this; - } - - public TestFile assertIsEmptyDir() { - if (exists()) { - assertIsDir(); - assertHasDescendants(); - } - return this; - } - - private void visit(Set names, String prefix, File file) { - for (File child : file.listFiles()) { - if (child.isFile()) { - names.add(prefix + child.getName()); - } else if (child.isDirectory()) { - visit(names, prefix + child.getName() + "/", child); - } - } - } - - public boolean isSelfOrDescendent(File file) { - if (file.getAbsolutePath().equals(getAbsolutePath())) { - return true; - } - return file.getAbsolutePath().startsWith(getAbsolutePath() + File.separatorChar); - } - - public TestFile createDir() { - if (mkdirs()) { - return this; - } - if (isDirectory()) { - return this; - } - throw new AssertionError("Problems creating dir: " + this - + ". Diagnostics: exists=" + this.exists() + ", isFile=" + this.isFile() + ", isDirectory=" + this.isDirectory()); - } - - public TestFile createDir(Object path) { - return new TestFile(this, path).createDir(); - } - - public TestFile deleteDir() { - new TestFileHelper(this).delete(useNativeTools); - return this; - } - - /** - * Attempts to delete this directory, ignoring failures to do so. - * - * @return this - */ - public TestFile maybeDeleteDir() { - try { - deleteDir(); - } catch (RuntimeException e) { - // Ignore - } - return this; - } - - public TestFile createFile() { - new TestFile(getParentFile()).createDir(); - try { - assertTrue(isFile() || createNewFile()); - } catch (IOException e) { - throw new RuntimeException(e); - } - return this; - } - - public TestFile createFile(Object path) { - return file(path).createFile(); - } - - public TestFile createZip(Object path) { - Zip zip = new Zip(); - zip.setWhenempty((Zip.WhenEmpty) Zip.WhenEmpty.getInstance(Zip.WhenEmpty.class, "create")); - TestFile zipFile = file(path); - zip.setDestFile(zipFile); - zip.setBasedir(this); - zip.setExcludes("**"); - execute(zip); - return zipFile; - } - - public TestFile zipTo(TestFile zipFile) { - new TestFileHelper(this).zipTo(zipFile, useNativeTools); - return this; - } - - public TestFile tarTo(TestFile tarFile) { - new TestFileHelper(this).tarTo(tarFile, useNativeTools); - return this; - } - - public TestFile tgzTo(TestFile tarFile) { - Tar tar = new Tar(); - tar.setBasedir(this); - tar.setDestFile(tarFile); - tar.setCompression((Tar.TarCompressionMethod) EnumeratedAttribute.getInstance(Tar.TarCompressionMethod.class, "gzip")); - execute(tar); - return this; - } - - public TestFile tbzTo(TestFile tarFile) { - Tar tar = new Tar(); - tar.setBasedir(this); - tar.setDestFile(tarFile); - tar.setCompression((Tar.TarCompressionMethod) EnumeratedAttribute.getInstance(Tar.TarCompressionMethod.class, "bzip2")); - execute(tar); - return this; - } - - private void execute(Task task) { - task.setProject(new Project()); - task.execute(); - } - - public Snapshot snapshot() { - assertIsFile(); - return new Snapshot(lastModified(), getHash("MD5")); - } - - public void assertHasChangedSince(Snapshot snapshot) { - Snapshot now = snapshot(); - assertTrue(now.modTime != snapshot.modTime || !Arrays.equals(now.hash, snapshot.hash)); - } - - public void assertContentsHaveChangedSince(Snapshot snapshot) { - Snapshot now = snapshot(); - assertFalse(Arrays.equals(now.hash, snapshot.hash), String.format("contents of %s have not changed", this)); - } - - public void assertContentsHaveNotChangedSince(Snapshot snapshot) { - Snapshot now = snapshot(); - assertArrayEquals(snapshot.hash, now.hash, String.format("contents of %s has changed", this)); - } - - public void assertHasNotChangedSince(Snapshot snapshot) { - Snapshot now = snapshot(); - assertEquals(snapshot.modTime, now.modTime, String.format("last modified time of %s has changed", this)); - assertArrayEquals(snapshot.hash, now.hash, String.format("contents of %s has changed", this)); - } - - public void writeProperties(Map properties) { - Properties props = new Properties(); - props.putAll(properties); - try { - try (FileOutputStream stream = new FileOutputStream(this)) { - props.store(stream, "comment"); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public ExecOutput exec(Object... args) { - return new TestFileHelper(this).execute(Arrays.asList(args), null); - } - - public ExecOutput execute(List args, List env) { - return new TestFileHelper(this).execute(args, env); - } - - public static class Snapshot { - private final long modTime; - private final byte[] hash; - - public Snapshot(long modTime, byte[] hash) { - this.modTime = modTime; - this.hash = hash; - } - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy deleted file mode 100644 index 414f09132..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestFileHelper.groovy +++ /dev/null @@ -1,203 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.file - -import org.apache.commons.io.FileUtils -import org.apache.commons.lang3.StringUtils -import org.apache.tools.ant.Project -import org.apache.tools.ant.taskdefs.Expand -import org.apache.tools.ant.taskdefs.Tar -import org.apache.tools.ant.taskdefs.Untar -import org.apache.tools.ant.taskdefs.Zip - -import java.util.zip.ZipInputStream - -import static org.junit.jupiter.api.Assertions.assertTrue - -class TestFileHelper { - TestFile file - - TestFileHelper(TestFile file) { - this.file = file - } - - void unzipTo(File target, boolean nativeTools) { - // Check that each directory in hierarchy is present - file.withInputStream { InputStream instr -> - def dirs = [] as Set - def zipStr = new ZipInputStream(instr) - def entry - while (entry = zipStr.getNextEntry()) { - if (entry.directory) { - assertTrue(dirs.add(entry.name), "Duplicate directory '$entry.name'") - } - if (!entry.name.contains('/')) { - continue - } - def parent = StringUtils.substringBeforeLast(entry.name, '/') + '/' - assertTrue(dirs.contains(parent), "Missing dir '$parent'") - } - } - - if (nativeTools && isUnix()) { - def process = ['unzip', '-o', file.absolutePath, '-d', target.absolutePath].execute() - process.consumeProcessOutput(System.out, System.err) - assert process.waitFor() == 0 - return - } - - def unzip = new Expand() - unzip.src = file - unzip.dest = target - - unzip.project = new Project() - unzip.execute() - } - - void untarTo(File target, boolean nativeTools) { - if (nativeTools && isUnix()) { - target.mkdirs() - def builder = new ProcessBuilder(['tar', '-xpf', file.absolutePath]) - builder.directory(target) - def process = builder.start() - process.consumeProcessOutput() - assert process.waitFor() == 0 - return - } - - def untar = new Untar() - untar.setSrc(file) - untar.setDest(target) - - if (file.name.endsWith(".tgz")) { - def method = new Untar.UntarCompressionMethod() - method.value = "gzip" - untar.compression = method - } else if (file.name.endsWith(".tbz2")) { - def method = new Untar.UntarCompressionMethod() - method.value = "bzip2" - untar.compression = method - } - - untar.project = new Project() - untar.execute() - } - - private static boolean isUnix() { - return !System.getProperty('os.name').toLowerCase().contains('windows') - } - - String getPermissions() { - if (!isUnix()) { - return "-rwxr-xr-x" - } - - def process = ["ls", "-ld", file.absolutePath].execute() - def result = process.inputStream.text - def error = process.errorStream.text - def retval = process.waitFor() - if (retval != 0) { - throw new RuntimeException("Could not list permissions for '$file': $error") - } - def perms = result.split()[0] - assert perms.matches("[d\\-][rwx\\-]{9}[@\\+]?") - return perms.substring(1, 10) - } - - void setPermissions(String permissions) { - if (!isUnix()) { - return - } - int m = toMode(permissions) - setMode(m) - } - - void setMode(int mode) { - def process = ["chmod", Integer.toOctalString(mode), file.absolutePath].execute() - def error = process.errorStream.text - def retval = process.waitFor() - if (retval != 0) { - throw new RuntimeException("Could not set permissions for '$file': $error") - } - } - - private static int toMode(String permissions) { - int m = [6, 3, 0].inject(0) { mode, pos -> - mode |= permissions[9 - pos - 3] == 'r' ? 4 << pos : 0 - mode |= permissions[9 - pos - 2] == 'w' ? 2 << pos : 0 - mode |= permissions[9 - pos - 1] == 'x' ? 1 << pos : 0 - return mode - } - return m - } - - int getMode() { - return toMode(getPermissions()) - } - - void delete(boolean nativeTools) { - if (isUnix() && nativeTools) { - def process = ["rm", "-rf", file.absolutePath].execute() - def error = process.errorStream.text - def retval = process.waitFor() - if (retval != 0) { - throw new RuntimeException("Could not delete '$file': $error") - } - } else { - FileUtils.deleteQuietly(file) - } - } - - String readLink() { - def process = ["readlink", file.absolutePath].execute() - def error = process.errorStream.text - def retval = process.waitFor() - if (retval != 0) { - throw new RuntimeException("Could not read link '$file': $error") - } - return process.inputStream.text.trim() - } - - ExecOutput exec(List args) { - return execute(args, null) - } - - ExecOutput execute(List args, List env) { - def process = ([file.absolutePath] + args).execute(env, null) - String output = process.inputStream.text - String error = process.errorStream.text - if (process.waitFor() != 0) { - throw new RuntimeException("Could not execute $file. Error: $error, Output: $output") - } - return new ExecOutput(output, error) - } - - void zipTo(TestFile zipFile, boolean nativeTools) { - if (nativeTools && isUnix()) { - def process = ['zip', zipFile.absolutePath, "-r", file.name].execute(null, zipFile.parentFile) - process.consumeProcessOutput(System.out, System.err) - assert process.waitFor() == 0 - } else { - Zip zip = new Zip() - zip.setBasedir(file) - zip.setDestFile(zipFile) - zip.setProject(new Project()) - def whenEmpty = new Zip.WhenEmpty() - whenEmpty.setValue("create") - zip.setWhenempty(whenEmpty) - zip.execute() - } - } - - void tarTo(TestFile tarFile, boolean nativeTools) { - if (nativeTools && isUnix()) { - def process = ['tar', "-cf", tarFile.absolutePath, file.name].execute(null, tarFile.parentFile) - process.consumeProcessOutput(System.out, System.err) - assert process.waitFor() == 0 - } else { - Tar tar = new Tar() - tar.setBasedir(file) - tar.setDestFile(tarFile) - tar.setProject(new Project()) - tar.execute() - } - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy deleted file mode 100644 index 2af22f3e9..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/file/TestWorkspaceBuilder.groovy +++ /dev/null @@ -1,38 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.file - -/** - * Used in TestFile.create(). - * - * Should be inner class of TestFile, but can't because Groovy has issues with inner classes as delegates. - */ -class TestWorkspaceBuilder { - TestFile baseDir - - TestWorkspaceBuilder(TestFile baseDir) { - this.baseDir = baseDir - } - - def apply(Closure cl) { - cl.delegate = this - cl.resolveStrategy = Closure.DELEGATE_FIRST - cl() - } - - def file(String name) { - TestFile file = baseDir.file(name) - file.write('some content') - file - } - - def setMode(int mode) { - baseDir.mode = mode - } - - def methodMissing(String name, Object args) { - if (args.length == 1 && args[0] instanceof Closure) { - baseDir.file(name).create(args[0]) - } else { - throw new MissingMethodException(name, getClass(), args) - } - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy index ea5504ec7..7e5b983d0 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy @@ -1,15 +1,14 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo import com.github.jengelman.gradle.plugins.shadow.util.HashUtil -import com.github.jengelman.gradle.plugins.shadow.util.file.TestFile abstract class AbstractModule { /** * @param cl A closure that is passed a writer to use to generate the content. */ - protected void publish(TestFile file, Closure cl) { + protected void publish(File file, Closure cl) { def hashBefore = file.exists() ? getHash(file, "sha1") : null - def tmpFile = file.parentFile.file("${file.name}.tmp") + def tmpFile = file.parentFile.resolve("${file.name}.tmp") tmpFile.withWriter("utf-8") { cl.call(it) @@ -26,9 +25,9 @@ abstract class AbstractModule { onPublish(file) } - protected void publishWithStream(TestFile file, Closure cl) { + protected void publishWithStream(File file, Closure cl) { def hashBefore = file.exists() ? getHash(file, "sha1") : null - def tmpFile = file.parentFile.file("${file.name}.tmp") + def tmpFile = file.parentFile.resolve("${file.name}.tmp") tmpFile.withOutputStream { cl.call(it) @@ -45,36 +44,36 @@ abstract class AbstractModule { onPublish(file) } - protected abstract onPublish(TestFile file) + protected abstract onPublish(File file) - static TestFile getSha1File(TestFile file) { + static File getSha1File(File file) { getHashFile(file, "sha1") } - static TestFile sha1File(TestFile file) { + static File sha1File(File file) { hashFile(file, "sha1", 40) } - static TestFile getMd5File(TestFile file) { + static File getMd5File(File file) { getHashFile(file, "md5") } - static TestFile md5File(TestFile file) { + static File md5File(File file) { hashFile(file, "md5", 32) } - private static TestFile hashFile(TestFile file, String algorithm, int len) { + private static File hashFile(File file, String algorithm, int len) { def hashFile = getHashFile(file, algorithm) def hash = getHash(file, algorithm) hashFile.text = String.format("%0${len}x", hash) return hashFile } - private static TestFile getHashFile(TestFile file, String algorithm) { - file.parentFile.file("${file.name}.${algorithm}") + private static File getHashFile(File file, String algorithm) { + file.parentFile.resolve("${file.name}.${algorithm}") } - private static BigInteger getHash(TestFile file, String algorithm) { + private static BigInteger getHash(File file, String algorithm) { HashUtil.createHash(file, algorithm.toUpperCase()).asBigInteger() } -} \ No newline at end of file +} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy index 16a2fffd6..e08a29ad9 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven -import com.github.jengelman.gradle.plugins.shadow.util.file.TestFile import com.github.jengelman.gradle.plugins.shadow.util.repo.AbstractModule import groovy.xml.MarkupBuilder import groovy.xml.XmlParser @@ -9,7 +8,7 @@ import java.text.SimpleDateFormat abstract class AbstractMavenModule extends AbstractModule implements MavenModule { protected static final String MAVEN_METADATA_FILE = "maven-metadata.xml" - final TestFile moduleDir + final File moduleDir final String groupId final String artifactId final String version @@ -22,7 +21,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule final updateFormat = new SimpleDateFormat("yyyyMMddHHmmss") final timestampFormat = new SimpleDateFormat("yyyyMMdd.HHmmss") - AbstractMavenModule(TestFile moduleDir, String groupId, String artifactId, String version) { + AbstractMavenModule(File moduleDir, String groupId, String artifactId, String version) { this.moduleDir = moduleDir this.groupId = groupId this.artifactId = artifactId @@ -42,10 +41,10 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule } @Override - TestFile getArtifactFile(Map options = [:]) { + File getArtifactFile(Map options = [:]) { if (version.endsWith("-SNAPSHOT") && !metaDataFile.exists() && uniqueSnapshots) { def artifact = toArtifact(options) - return moduleDir.file("${artifactId}-${version}${artifact.classifier ? "-${artifact.classifier}" : ""}.${artifact.type}") + return moduleDir.resolve("${artifactId}-${version}${artifact.classifier ? "-${artifact.classifier}" : ""}.${artifact.type}") } return artifactFile(options) } @@ -188,26 +187,26 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule } @Override - TestFile getPomFile() { - return moduleDir.file("$artifactId-${publishArtifactVersion}.pom") + File getPomFile() { + return moduleDir.resolve("$artifactId-${publishArtifactVersion}.pom") } @Override - TestFile getMetaDataFile() { - moduleDir.file(MAVEN_METADATA_FILE) + File getMetaDataFile() { + moduleDir.resolve(MAVEN_METADATA_FILE) } - TestFile getRootMetaDataFile() { - moduleDir.parentFile.file(MAVEN_METADATA_FILE) + File getRootMetaDataFile() { + moduleDir.parentFile.resolve(MAVEN_METADATA_FILE) } - TestFile artifactFile(Map options) { + File artifactFile(Map options) { def artifact = toArtifact(options) def fileName = "$artifactId-${publishArtifactVersion}.${artifact.type}" if (artifact.classifier) { fileName = "$artifactId-$publishArtifactVersion-${artifact.classifier}.${artifact.type}" } - return moduleDir.file(fileName) + return moduleDir.resolve(fileName) } @Override @@ -282,7 +281,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule return this } - private void updateRootMavenMetaData(TestFile rootMavenMetaData) { + private void updateRootMavenMetaData(File rootMavenMetaData) { def allVersions = rootMavenMetaData.exists() ? new XmlParser().parseText(rootMavenMetaData.text).versioning.versions.version*.value().flatten() : [] allVersions << version publish(rootMavenMetaData) { Writer writer -> diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy index 67a697f77..71233befb 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy @@ -1,11 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven -import com.github.jengelman.gradle.plugins.shadow.util.file.TestFile class MavenFileModule extends AbstractMavenModule { private boolean uniqueSnapshots = true - MavenFileModule(TestFile moduleDir, String groupId, String artifactId, String version) { + MavenFileModule(File moduleDir, String groupId, String artifactId, String version) { super(moduleDir, groupId, artifactId, version) } @@ -40,7 +39,7 @@ class MavenFileModule extends AbstractMavenModule { } @Override - protected onPublish(TestFile file) { + protected onPublish(File file) { sha1File(file) md5File(file) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy index f72d0cd09..455588315 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy @@ -1,14 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven -import com.github.jengelman.gradle.plugins.shadow.util.file.TestFile /** * A fixture for dealing with file Maven repositories. */ class MavenFileRepository implements MavenRepository { - final TestFile rootDir + final File rootDir - MavenFileRepository(TestFile rootDir) { + MavenFileRepository(File rootDir) { this.rootDir = rootDir } @@ -19,7 +18,7 @@ class MavenFileRepository implements MavenRepository { @Override MavenFileModule module(String groupId, String artifactId, Object version = '1.0') { - def artifactDir = rootDir.file("${groupId.replace('.', '/')}/$artifactId/$version") + def artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") return new MavenFileModule(artifactDir, groupId, artifactId, version as String) } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy index 2698e0a30..fcd466d10 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven -import com.github.jengelman.gradle.plugins.shadow.util.file.TestFile interface MavenModule { /** @@ -33,11 +32,11 @@ interface MavenModule { */ MavenModule hasType(String type) - TestFile getPomFile() + File getPomFile() - TestFile getArtifactFile() + File getArtifactFile() - TestFile getMetaDataFile() + File getMetaDataFile() MavenPom getParsedPom() diff --git a/src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule b/src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule new file mode 100644 index 000000000..bb7d5044f --- /dev/null +++ b/src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule @@ -0,0 +1,3 @@ +moduleName = groovy-extensions +moduleVersion = ${moduleVersion} +extensionClasses =com.github.jengelman.gradle.plugins.shadow.util.FileExtensions From 48dec45e2ef115e18e58276d9316416d96a6c0d2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 4 Dec 2024 03:43:30 -0500 Subject: [PATCH 070/941] Revert "Add a new source set to share test kits" (#1085) This reverts commit 6c1920568d4c9c98ed5bb86ebf106a92ff63ef01. --- build.gradle.kts | 10 +--------- .../PropertiesFileTransformerSpec.groovy | 13 ++++++------- .../transformers/TransformerSpecSupport.groovy | 3 +++ .../ApacheNoticeResourceTransformerTest.kt | 2 +- .../shadow/transformers/AppendingTransformerTest.kt | 2 +- .../transformers/ManifestAppenderTransformerTest.kt | 2 +- .../transformers/PropertiesFileTransformerTest.kt | 2 +- .../transformers/XmlAppendingTransformerTest.kt | 2 +- .../jengelman/gradle/plugins/shadow}/util/Utils.kt | 4 +--- 9 files changed, 16 insertions(+), 24 deletions(-) rename src/{testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit => test/kotlin/com/github/jengelman/gradle/plugins/shadow}/util/Utils.kt (65%) diff --git a/build.gradle.kts b/build.gradle.kts index bf84d0158..aa4265751 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,9 +43,6 @@ spotless { } } -val testKit: SourceSet by sourceSets.creating -val testKitImplementation: Configuration by configurations.getting - val intiTest: SourceSet by sourceSets.creating val intiTestImplementation: Configuration by configurations.getting { extendsFrom(configurations.testImplementation.get()) @@ -77,11 +74,6 @@ dependencies { implementation(libs.plexus.utils) implementation(libs.plexus.xml) - val mainOutput = sourceSets.main.map { it.output } - testKitImplementation(mainOutput) - testKitImplementation(gradleTestKit()) - - testImplementation(testKit.output) testImplementation(platform(libs.junit.bom)) testImplementation(libs.junit.jupiter) testImplementation(libs.assertk) @@ -93,7 +85,7 @@ dependencies { exclude(group = "org.codehaus.groovy") exclude(group = "org.hamcrest") } - funcTestImplementation(mainOutput) + funcTestImplementation(sourceSets.main.get().output) lintChecks(libs.androidx.gradlePluginLints) lintChecks(libs.assertk.lint) diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy index 6fc067a6b..72a5e40c3 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy @@ -22,7 +22,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import spock.lang.Unroll -import static com.github.jengelman.gradle.plugins.shadow.testkit.util.Utils.testObjectFactory import static groovy.lang.Closure.IDENTITY @Unroll @@ -30,7 +29,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void "Path #path #transform transformed"() { given: - Transformer transformer = new PropertiesFileTransformer(testObjectFactory) + Transformer transformer = new PropertiesFileTransformer(objectFactory) when: boolean actual = transformer.canTransformResource(getFileElement(path)) @@ -50,7 +49,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void exerciseAllTransformConfigurations() { given: def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(testObjectFactory) + Transformer transformer = new PropertiesFileTransformer(objectFactory) transformer.mergeStrategy.set(MergeStrategy.from(mergeStrategy)) transformer.mergeSeparator.set(mergeSeparator) @@ -74,7 +73,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void exerciseAllTransformConfigurationsWithPaths() { given: def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(testObjectFactory) + Transformer transformer = new PropertiesFileTransformer(objectFactory) transformer.paths.set(paths) transformer.mergeStrategy.set(MergeStrategy.from('first')) @@ -98,7 +97,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void exerciseAllTransformConfigurationsWithMappings() { given: def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(testObjectFactory) + Transformer transformer = new PropertiesFileTransformer(objectFactory) transformer.mappings.set(mappings) transformer.mergeStrategy.set(MergeStrategy.from('latest')) @@ -124,7 +123,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void appliesKeyTransformer() { given: def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(testObjectFactory) + Transformer transformer = new PropertiesFileTransformer(objectFactory) transformer.keyTransformer.set(keyTransformer) transformer.mergeStrategy.set(MergeStrategy.from('append')) @@ -148,7 +147,7 @@ class PropertiesFileTransformerSpec extends TransformerSpecSupport { void appliesCharset() { given: def element = getFileElement(path) - def transformer = new PropertiesFileTransformer(testObjectFactory) + def transformer = new PropertiesFileTransformer(objectFactory) transformer.charsetName.set(charset) when: diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy index 86978937c..818697e0b 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy @@ -4,11 +4,14 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowStats import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath import org.gradle.api.internal.file.DefaultFileTreeElement +import org.gradle.testfixtures.ProjectBuilder import spock.lang.Shared import spock.lang.Specification class TransformerSpecSupport extends Specification { + protected static final def objectFactory = ProjectBuilder.builder().build().objects + @Shared ShadowStats stats diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt index f8cf651ff..05d013dbf 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -4,7 +4,7 @@ import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue import assertk.fail -import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 681000705..b5eee7d89 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -3,7 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt index 935c98da6..9c1e8c084 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -6,7 +6,7 @@ import assertk.assertions.isFalse import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index 81e084d43..3106cc9ba 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -6,7 +6,7 @@ import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isNotEmpty import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index 32895d9fc..1eaf76436 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -3,7 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.testkit.util.testObjectFactory +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/util/Utils.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt similarity index 65% rename from src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/util/Utils.kt rename to src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt index 0785681b0..a6acfd481 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/util/Utils.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt @@ -1,6 +1,4 @@ -@file:JvmName("Utils") - -package com.github.jengelman.gradle.plugins.shadow.testkit.util +package com.github.jengelman.gradle.plugins.shadow.util import org.gradle.api.model.ObjectFactory import org.gradle.testfixtures.ProjectBuilder From 8e09bc1df71c98b706c76f8979e4643ddfb4bef1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 4 Dec 2024 03:53:19 -0500 Subject: [PATCH 071/941] Add a factory function for creating transformers (#1086) --- api/shadow.api | 6 ++++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 13 ++----------- .../plugins/shadow/transformers/Transformer.kt | 15 +++++++++++++++ .../ApacheLicenseResourceTransformerTest.kt | 6 ------ .../ApacheNoticeResourceTransformerTest.kt | 7 ------- .../transformers/AppendingTransformerTest.kt | 7 ------- .../ComponentsXmlResourceTransformerTest.kt | 7 ------- .../ManifestAppenderTransformerTest.kt | 7 ------- .../transformers/PropertiesFileTransformerTest.kt | 8 -------- .../shadow/transformers/TransformerTestSupport.kt | 12 ++++++++++++ .../transformers/XmlAppendingTransformerTest.kt | 7 ------- 11 files changed, 35 insertions(+), 60 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 0b2dea4c4..9b750800c 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -556,7 +556,9 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFile } public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer : org/gradle/api/Named { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer$Companion; public abstract fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public static fun create (Ljava/lang/Class;Lorg/gradle/api/model/ObjectFactory;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer; public fun getName ()Ljava/lang/String; public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public abstract fun hasTransformedResource ()Z @@ -564,6 +566,10 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/trans public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } +public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$Companion { + public final fun create (Ljava/lang/Class;Lorg/gradle/api/model/ObjectFactory;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer; +} + public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$DefaultImpls { public static fun getName (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Ljava/lang/String; public static fun getObjectFactory (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lorg/gradle/api/model/ObjectFactory; diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index b50b52392..811330245 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -16,6 +16,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransfor import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create import java.util.jar.JarFile import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action @@ -27,7 +28,6 @@ import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.DefaultCopySpec -import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask @@ -173,16 +173,7 @@ public abstract class ShadowJar : } override fun transform(clazz: Class, action: Action?): ShadowJar = apply { - // If the constructor takes a single ObjectFactory, inject it in. - val constructor = clazz.constructors.find { - it.parameterTypes.singleOrNull() == ObjectFactory::class.java - } - val transformer = if (constructor != null) { - objectFactory.newInstance(clazz) - } else { - clazz.getDeclaredConstructor().newInstance() - } - addTransform(transformer, action) + addTransform(clazz.create(objectFactory), action) } override fun transform(transformer: Transformer): ShadowJar = apply { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index 4fea17f84..93fcd0e21 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -34,6 +34,21 @@ public interface Transformer : Named { @get:Internal public val objectFactory: ObjectFactory get() = throw NotImplementedError("You have to make sure this has been implemented or injected.") + + public companion object { + @JvmStatic + public fun Class.create(objectFactory: ObjectFactory): T { + // If the constructor takes a single ObjectFactory, inject it in. + val constructor = constructors.find { + it.parameterTypes.singleOrNull() == ObjectFactory::class.java + } + return if (constructor != null) { + objectFactory.newInstance(this@create) + } else { + getDeclaredConstructor().newInstance() + } + } + } } public object NoOpTransformer : Transformer { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt index 407721fed..4d3b72b41 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test /** @@ -15,11 +14,6 @@ class ApacheLicenseResourceTransformerTest : TransformerTestSupport() setupTurkishLocale() } - @BeforeEach - fun setup() { - transformer = AppendingTransformer(testObjectFactory) - } - @Test fun testCanTransformResource() { transformer.resource.set("abcdefghijklmnopqrstuvwxyz") diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt index 9045a89b2..d419e84e1 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -3,19 +3,12 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isTrue import org.custommonkey.xmlunit.XMLUnit -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test /** * Modified from [org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformerTest.java). */ class ComponentsXmlResourceTransformerTest : TransformerTestSupport() { - - @BeforeEach - fun setup() { - transformer = ComponentsXmlResourceTransformer() - } - @Test fun testConfigurationMerging() { XMLUnit.setNormalizeWhitespace(true) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt index 9c1e8c084..c36db4cc0 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -6,16 +6,9 @@ import assertk.assertions.isFalse import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class ManifestAppenderTransformerTest : TransformerTestSupport() { - @BeforeEach - fun setup() { - transformer = ManifestAppenderTransformer(testObjectFactory) - } - @Test fun testCanTransformResource() { with(transformer) { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index 3106cc9ba..19128632b 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -6,17 +6,9 @@ import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isNotEmpty import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class PropertiesFileTransformerTest : TransformerTestSupport() { - - @BeforeEach - fun setup() { - transformer = PropertiesFileTransformer(testObjectFactory) - } - @Test fun testHasTransformedResource() { transformer.transform(manifestTransformerContext) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt index ab0be820d..1acc05f10 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt @@ -1,7 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import java.io.InputStream +import java.lang.reflect.ParameterizedType import java.nio.file.Path import java.util.Locale import java.util.zip.ZipFile @@ -10,9 +13,11 @@ import kotlin.io.path.outputStream import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath +import org.junit.jupiter.api.BeforeEach abstract class TransformerTestSupport { protected lateinit var transformer: T + private set protected val manifestTransformerContext: TransformerContext get() = TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME)) @@ -21,6 +26,13 @@ abstract class TransformerTestSupport { return this::class.java.classLoader.getResourceAsStream(name) ?: error("Resource $name not found.") } + @BeforeEach + fun setup() { + @Suppress("UNCHECKED_CAST") + val clazz = (this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments.first() as Class + transformer = clazz.create(testObjectFactory) + } + protected companion object { const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index 1eaf76436..b9eddbf31 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -3,8 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isFalse import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class XmlAppendingTransformerTest : TransformerTestSupport() { @@ -13,11 +11,6 @@ class XmlAppendingTransformerTest : TransformerTestSupport Date: Wed, 4 Dec 2024 06:40:30 -0500 Subject: [PATCH 072/941] Migrate the rest unit tests to Kotlin and Junit (#1084) * Convert Log4j2PluginsCacheFileTransformerSpec * Use byteInputStream * Add inputStream extension for Properties * Add util functions * Tweak requireResourceAsStream * Convert ServiceFileTransformerSpec * Convert PropertiesFileTransformerSpec * Merge PropertiesFileTransformerTest * Rename TransformerTestSupport to BaseTransformerTest * Cleanup Log4j2PluginsCacheFileTransformerTest * Cleanup ServiceFileTransformerTest * Cleanup PropertiesFileTransformerTest * Add canTransformResource extension * Cleanups * Fix typo * Rearrange context extensions * Tweak names --- ...g4j2PluginsCacheFileTransformerSpec.groovy | 85 ------ .../PropertiesFileTransformerSpec.groovy | 165 ---------- .../ServiceFileTransformerSpec.groovy | 63 ---- .../TransformerSpecSupport.groovy | 61 ---- .../shadow/util/PluginSpecification.groovy | 2 - .../gradle/plugins/shadow/internal/Utils.kt | 18 +- .../transformers/PropertiesFileTransformer.kt | 17 +- .../transformers/ServiceFileTransformer.kt | 4 +- .../shadow/relocation/SimpleRelocatorTest.kt | 14 +- .../ApacheLicenseResourceTransformerTest.kt | 14 +- .../ApacheNoticeResourceTransformerTest.kt | 16 +- .../transformers/AppendingTransformerTest.kt | 8 +- ...rTestSupport.kt => BaseTransformerTest.kt} | 15 +- .../ComponentsXmlResourceTransformerTest.kt | 2 +- .../Log4j2PluginsCacheFileTransformerTest.kt | 55 ++++ .../ManifestAppenderTransformerTest.kt | 6 +- .../PropertiesFileTransformerTest.kt | 288 +++++++++++++++++- .../ServiceFileTransformerTest.kt | 64 ++++ .../XmlAppendingTransformerTest.kt | 8 +- 19 files changed, 475 insertions(+), 430 deletions(-) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy rename src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/{TransformerTestSupport.kt => BaseTransformerTest.kt} (79%) create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy deleted file mode 100644 index abacd4498..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerSpec.groovy +++ /dev/null @@ -1,85 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator -import org.apache.logging.log4j.core.config.plugins.processor.PluginCache -import org.apache.tools.zip.ZipOutputStream -import spock.lang.Specification - -import static java.util.Collections.singletonList -import static org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE - -/** - * @author Paul Nelson Baker - * @since 2018-08 - * @see GitHub - * @see LinkedIn - */ -class Log4j2PluginsCacheFileTransformerSpec extends Specification { - - Log4j2PluginsCacheFileTransformer transformer - - void setup() { - transformer = new Log4j2PluginsCacheFileTransformer() - } - - void "should transform for a single file"() { - when: - transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE))) - - then: - transformer.hasTransformedResource() - } - - void "should transform"() { - given: - def relocators = singletonList(new SimpleRelocator()) - - when: - transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators)) - - then: - transformer.hasTransformedResource() - } - - void "relocate classes inside DAT file"() { - given: - String pattern = "org.apache.logging" - String destination = "new.location.org.apache.logging" - - List relocators = singletonList(new SimpleRelocator(pattern, destination)) - - when: - transformer.transform(new TransformerContext(PLUGIN_CACHE_FILE, getResourceStream(PLUGIN_CACHE_FILE), relocators)) - - then: - transformer.hasTransformedResource() - - when: - // Write out to a fake jar file - def testableZipFile = File.createTempFile("testable-zip-file-", ".jar") - def fileOutputStream = new FileOutputStream(testableZipFile) - def bufferedOutputStream = new BufferedOutputStream(fileOutputStream) - def zipOutputStream = new ZipOutputStream(bufferedOutputStream) - - transformer.modifyOutputStream(zipOutputStream, true) - - zipOutputStream.close() - bufferedOutputStream.close() - fileOutputStream.close() - - then: - // Pull the data back out and make sure it was transformed - PluginCache cache = new PluginCache() - def urlString = "jar:" + testableZipFile.toURI().toURL() + "!/" + PLUGIN_CACHE_FILE - cache.loadCacheFiles(Collections.enumeration([new URL(urlString)])) - - cache.getCategory("lookup")["date"].className == "new.location.org.apache.logging.log4j.core.lookup.DateLookup" - - } - - InputStream getResourceStream(String resource) { - return this.class.getClassLoader().getResourceAsStream(resource) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy deleted file mode 100644 index 72a5e40c3..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerSpec.groovy +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License") you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy -import spock.lang.Unroll - -import static groovy.lang.Closure.IDENTITY - -@Unroll -class PropertiesFileTransformerSpec extends TransformerSpecSupport { - - void "Path #path #transform transformed"() { - given: - Transformer transformer = new PropertiesFileTransformer(objectFactory) - - when: - boolean actual = transformer.canTransformResource(getFileElement(path)) - - then: - actual == expected - - where: - path || expected - 'foo.properties' || true - 'foo/bar.properties' || true - 'foo.props' || false - - transform = expected ? 'can be' : 'can not be' - } - - void exerciseAllTransformConfigurations() { - given: - def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) - transformer.mergeStrategy.set(MergeStrategy.from(mergeStrategy)) - transformer.mergeSeparator.set(mergeSeparator) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | mergeStrategy | mergeSeparator | input1 | input2 || output - 'f.properties' | 'first' | '' | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'f.properties' | 'latest' | '' | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'bar'] - 'f.properties' | 'append' | ',' | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo,bar'] - 'f.properties' | 'append' | ';' | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo;bar'] - } - - void exerciseAllTransformConfigurationsWithPaths() { - given: - def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) - transformer.paths.set(paths) - transformer.mergeStrategy.set(MergeStrategy.from('first')) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | paths | input1 | input2 || output - 'f.properties' | ['f.properties'] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'foo.properties' | ['.*.properties'] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'foo.properties' | ['.*bar'] | ['foo': 'foo'] | ['foo': 'bar'] || [:] - 'foo.properties' | [] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - } - - void exerciseAllTransformConfigurationsWithMappings() { - given: - def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) - transformer.mappings.set(mappings) - transformer.mergeStrategy.set(MergeStrategy.from('latest')) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | mappings | input1 | input2 || output - 'f.properties' | ['f.properties': [mergeStrategy: 'first']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'f.properties' | ['f.properties': [mergeStrategy: 'latest']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'bar'] - 'f.properties' | ['f.properties': [mergeStrategy: 'append']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo,bar'] - 'f.properties' | ['f.properties': [mergeStrategy: 'append', mergeSeparator: ';']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo;bar'] - 'foo.properties' | ['.*.properties': [mergeStrategy: 'first']] | ['foo': 'foo'] | ['foo': 'bar'] || ['foo': 'foo'] - 'foo.properties' | ['.*bar': [mergeStrategy: 'first']] | ['foo': 'foo'] | ['foo': 'bar'] || [:] - } - - void appliesKeyTransformer() { - given: - def element = getFileElement(path) - Transformer transformer = new PropertiesFileTransformer(objectFactory) - transformer.keyTransformer.set(keyTransformer) - transformer.mergeStrategy.set(MergeStrategy.from('append')) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | keyTransformer | input1 | input2 || output - 'foo.properties' | IDENTITY | ['foo': 'bar'] | ['FOO': 'baz'] || ['foo': 'bar', 'FOO': 'baz'] - 'foo.properties' | { key -> key.toUpperCase() } | ['foo': 'bar'] | ['FOO': 'baz'] || ['FOO': 'bar,baz'] - 'foo.properties' | { key -> 'bar.' + key.toLowerCase() } | ['foo': 'bar'] | ['FOO': 'baz'] || ['bar.foo': 'bar,baz'] - 'foo.properties' | { key -> key.replaceAll('^(foo)', 'bar.$1') } | ['foo': 'bar'] | ['FOO': 'baz'] || ['bar.foo': 'bar', 'FOO': 'baz'] - } - - void appliesCharset() { - given: - def element = getFileElement(path) - def transformer = new PropertiesFileTransformer(objectFactory) - transformer.charsetName.set(charset) - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input, charset)) - } - - then: - output == toMap(transformer.propertiesEntries[path]) - - where: - path | charset | input || output - 'utf8.properties' | 'utf-8' | ['foo': '传傳磨宿说説'] || ['foo': '传傳磨宿说説'] - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy deleted file mode 100644 index edee227ee..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerSpec.groovy +++ /dev/null @@ -1,63 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import spock.lang.Unroll - -@Unroll -class ServiceFileTransformerSpec extends TransformerSpecSupport { - - def "#status path #path #transform transformed"() { - given: - def transformer = new ServiceFileTransformer() - if (exclude) { - transformer.exclude(path) - } - - when: - def actual = transformer.canTransformResource(getFileElement(path)) - - then: - actual == expected - - where: - path | exclude | expected - 'META-INF/services/java.sql.Driver' | false | true - 'META-INF/services/io.dropwizard.logging.AppenderFactory' | false | true - 'META-INF/services/org.apache.maven.Shade' | true | false - 'META-INF/services/foo/bar/moo.goo.Zoo' | false | true - 'foo/bar.properties' | false | false - 'foo.props' | false | false - - transform = expected ? 'can be' : 'can not be' - status = exclude ? 'excluded' : 'non-excluded' - } - - def "transforms service file"() { - given: - def element = getFileElement(path) - def transformer = new ServiceFileTransformer() - - when: - if (transformer.canTransformResource(element)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) - } - - then: - transformer.hasTransformedResource() - output == transformer.serviceEntries[path].toInputStream().text - - where: - path | input1 | input2 || output - 'META-INF/services/com.acme.Foo' | 'foo' | 'bar' || 'foo\nbar' - 'META-INF/services/com.acme.Bar' | 'foo\nbar' | 'zoo' || 'foo\nbar\nzoo' - } - - def "excludes Groovy extension module descriptor files by default"() { - given: - def transformer = new ServiceFileTransformer() - def element = getFileElement('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') - - expect: - !transformer.canTransformResource(element) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy deleted file mode 100644 index 818697e0b..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerSpecSupport.groovy +++ /dev/null @@ -1,61 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import org.gradle.api.file.FileTreeElement -import org.gradle.api.file.RelativePath -import org.gradle.api.internal.file.DefaultFileTreeElement -import org.gradle.testfixtures.ProjectBuilder -import spock.lang.Shared -import spock.lang.Specification - -class TransformerSpecSupport extends Specification { - - protected static final def objectFactory = ProjectBuilder.builder().build().objects - - @Shared - ShadowStats stats - - def setup() { - stats = new ShadowStats() - } - - protected static FileTreeElement getFileElement(String path) { - // TODO: this should be replace with `createDefaultFileTreeElement` once this test gets migrated to Kotlin. - return new DefaultFileTreeElement(null, RelativePath.parse(true, path), null, null) - } - - protected static InputStream toInputStream(String str) { - return new ByteArrayInputStream(str.bytes) - } - - protected static InputStream toInputStream(Properties props, String charset) { - ByteArrayOutputStream baos = new ByteArrayOutputStream() - baos.withWriter(charset) { w -> - props.store(w, '') - } - new ByteArrayInputStream(baos.toByteArray()) - } - - protected static Properties toProperties(Map map) { - map.inject(new Properties()) { Properties props, entry -> - props.put(entry.key, entry.value) - props - } - } - - protected static Map toMap(Properties props) { - props.inject([:]) { Map map, entry -> - map.put(entry.key, entry.value) - map - } - } - - protected TransformerContext context(String path, Map input, String charset = 'ISO_8859_1') { - TransformerContext.builder().path(path).inputStream(toInputStream(toProperties(input), charset)).stats(stats).build() - } - - protected TransformerContext context(String path, String input) { - TransformerContext.builder().path(path).inputStream(toInputStream(input)).stats(stats).build() - } - -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy index 23b88c502..81678db82 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy @@ -16,8 +16,6 @@ abstract class PluginSpecification extends Specification { @TempDir Path dir - public static final String SHADOW_VERSION = System.getProperty("shadowVersion") - AppendableMavenFileRepository repo def setup() { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 0871a3a6c..61d774cd6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -1,7 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream import java.io.File +import java.io.FileNotFoundException import java.io.InputStream +import java.nio.charset.Charset +import java.util.Properties import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.file.RelativePath @@ -45,12 +50,23 @@ internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy { return lazy(LazyThreadSafetyMode.NONE, initializer) } +internal fun Properties.inputStream( + charset: Charset = Charsets.ISO_8859_1, + comments: String = "", +): ByteArrayInputStream { + val os = ByteArrayOutputStream() + os.writer(charset).use { writer -> + store(writer, comments) + } + return os.toByteArray().inputStream() +} + internal fun Class<*>.requireResourceAsText(name: String): String { return requireResourceAsStream(name).bufferedReader().readText() } private fun Class<*>.requireResourceAsStream(name: String): InputStream { - return getResourceAsStream(name) ?: error("Resource $name not found.") + return getResourceAsStream(name) ?: throw FileNotFoundException("Resource $name not found.") } private val DummyFile = File("dummy") diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index b09a42921..1e3a80a5d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -1,13 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties +import com.github.jengelman.gradle.plugins.shadow.internal.inputStream import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import groovy.lang.Closure import groovy.lang.Closure.IDENTITY -import java.io.ByteArrayOutputStream import java.io.InputStream -import java.io.InputStreamReader import java.nio.charset.Charset import java.util.Properties import javax.inject.Inject @@ -102,9 +101,11 @@ import org.gradle.api.tasks.Internal public open class PropertiesFileTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : Transformer { - private val propertiesEntries = mutableMapOf() private inline val charset get() = Charset.forName(charsetName.get()) + @get:Internal + internal val propertiesEntries = mutableMapOf() + @get:Input public open val paths: ListProperty = objectFactory.listProperty(String::class.java) @@ -226,7 +227,7 @@ public open class PropertiesFileTransformer @Inject constructor( val entry = ZipEntry(path) entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) - props.toReader().use { + props.inputStream(charset).reader(charset).use { it.copyTo(zipWriter) } zipWriter.flush() @@ -234,14 +235,6 @@ public open class PropertiesFileTransformer @Inject constructor( } } - private fun Properties.toReader(): InputStreamReader { - val os = ByteArrayOutputStream() - os.writer(charset).use { writer -> - store(writer, "") - } - return os.toByteArray().inputStream().reader(charset) - } - public enum class MergeStrategy { First, Latest, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 6d26c2a99..3f8023e64 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -11,6 +11,7 @@ import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal import org.gradle.api.tasks.util.PatternFilterable import org.gradle.api.tasks.util.PatternSet @@ -34,7 +35,8 @@ public open class ServiceFileTransformer( .exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN), ) : Transformer, PatternFilterable by patternSet { - private val serviceEntries = mutableMapOf() + @get:Internal + internal val serviceEntries = mutableMapOf() override fun canTransformResource(element: FileTreeElement): Boolean { val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index bf7c6cf3d..b5c9a070e 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -179,11 +179,13 @@ class SimpleRelocatorTest { .isEqualTo("META-INF/hidden.org.foo.xml") } - private fun SimpleRelocator.relocatePath(path: String): String { - return relocatePath(RelocatePathContext(path)) - } - - private fun SimpleRelocator.relocateClass(className: String): String { - return relocateClass(RelocateClassContext(className)) + private companion object { + fun SimpleRelocator.relocatePath(path: String): String { + return relocatePath(RelocatePathContext(path)) + } + + fun SimpleRelocator.relocateClass(className: String): String { + return relocateClass(RelocateClassContext(className)) + } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt index 4d3b72b41..3c2782e61 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test /** * Modified from [org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformerTest.java). */ -class ApacheLicenseResourceTransformerTest : TransformerTestSupport() { +class ApacheLicenseResourceTransformerTest : BaseTransformerTest() { init { setupTurkishLocale() @@ -16,11 +16,11 @@ class ApacheLicenseResourceTransformerTest : TransformerTestSupport() { +class ApacheNoticeResourceTransformerTest : BaseTransformerTest() { init { setupTurkishLocale() @@ -17,12 +17,12 @@ class ApacheNoticeResourceTransformerTest : TransformerTestSupport() { +class AppendingTransformerTest : BaseTransformerTest() { init { setupTurkishLocale() @@ -18,8 +18,8 @@ class AppendingTransformerTest : TransformerTestSupport() fun testCanTransformResource() { transformer.resource.set("abcdefghijklmnopqrstuvwxyz") - assertThat(transformer.canTransformResource(getFileElement("abcdefghijklmnopqrstuvwxyz"))).isTrue() - assertThat(transformer.canTransformResource(getFileElement("ABCDEFGHIJKLMNOPQRSTUVWXYZ"))).isTrue() - assertThat(transformer.canTransformResource(getFileElement("META-INF/MANIFEST.MF"))).isFalse() + assertThat(transformer.canTransformResource("abcdefghijklmnopqrstuvwxyz")).isTrue() + assertThat(transformer.canTransformResource("ABCDEFGHIJKLMNOPQRSTUVWXYZ")).isTrue() + assertThat(transformer.canTransformResource("META-INF/MANIFEST.MF")).isFalse() } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt similarity index 79% rename from src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt rename to src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 1acc05f10..18b0c4dbc 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerTestSupport.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -1,8 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory +import java.io.FileNotFoundException import java.io.InputStream import java.lang.reflect.ParameterizedType import java.nio.file.Path @@ -11,11 +13,10 @@ import java.util.zip.ZipFile import kotlin.io.path.createTempFile import kotlin.io.path.outputStream import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath import org.junit.jupiter.api.BeforeEach -abstract class TransformerTestSupport { +abstract class BaseTransformerTest { protected lateinit var transformer: T private set @@ -23,7 +24,7 @@ abstract class TransformerTestSupport { get() = TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME)) protected fun requireResourceAsStream(name: String): InputStream { - return this::class.java.classLoader.getResourceAsStream(name) ?: error("Resource $name not found.") + return this::class.java.classLoader.getResourceAsStream(name) ?: throw FileNotFoundException("Resource $name not found.") } @BeforeEach @@ -35,9 +36,11 @@ abstract class TransformerTestSupport { protected companion object { const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" + val sharedStats = ShadowStats() - fun getFileElement(path: String): FileTreeElement { - return createDefaultFileTreeElement(relativePath = RelativePath.parse(true, path)) + fun Transformer.canTransformResource(path: String): Boolean { + val element = createDefaultFileTreeElement(relativePath = RelativePath.parse(true, path)) + return canTransformResource(element) } fun readFrom(jarPath: Path, resourceName: String = MANIFEST_NAME): List { @@ -59,7 +62,7 @@ abstract class TransformerTestSupport { } /** - * NOTE: The Turkish locale has an usual case transformation for the letters "I" and "i", making it a prime + * NOTE: The Turkish locale has a usual case transformation for the letters "I" and "i", making it a prime * choice to test for improper case-less string comparisons. */ fun setupTurkishLocale() { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt index d419e84e1..43640d449 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -8,7 +8,7 @@ import org.junit.jupiter.api.Test /** * Modified from [org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformerTest.java). */ -class ComponentsXmlResourceTransformerTest : TransformerTestSupport() { +class ComponentsXmlResourceTransformerTest : BaseTransformerTest() { @Test fun testConfigurationMerging() { XMLUnit.setNormalizeWhitespace(true) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt new file mode 100644 index 000000000..48a4b1ff3 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -0,0 +1,55 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import java.io.File +import java.net.URL +import java.util.Collections +import org.apache.logging.log4j.core.config.plugins.processor.PluginCache +import org.apache.tools.zip.ZipOutputStream +import org.junit.jupiter.api.Test + +class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest() { + @Test + fun `should transform`() { + transformer.transform(context(SimpleRelocator())) + assertThat(transformer.hasTransformedResource()).isTrue() + } + + @Test + fun `should transform for a single file`() { + transformer.transform(context()) + assertThat(transformer.hasTransformedResource()).isTrue() + } + + @Test + fun `relocate classes inside DAT file`() { + val relocator = SimpleRelocator("org.apache.logging", "new.location.org.apache.logging") + transformer.transform(context(relocator)) + assertThat(transformer.hasTransformedResource()).isTrue() + + // Write out to a fake jar file + val testableZipFile = File.createTempFile("testable-zip-file-", ".jar") + ZipOutputStream(testableZipFile.outputStream().buffered()).use { zipOutputStream -> + transformer.modifyOutputStream(zipOutputStream, true) + } + + // Pull the data back out and make sure it was transformed + val cache = PluginCache() + val urlString = "jar:" + testableZipFile.toURI().toURL() + "!/" + PLUGIN_CACHE_FILE + cache.loadCacheFiles(Collections.enumeration(listOf(URL(urlString)))) + + assertThat(cache.getCategory("lookup")["date"]?.className) + .isEqualTo("new.location.org.apache.logging.log4j.core.lookup.DateLookup") + } + + private fun context(vararg relocator: SimpleRelocator): TransformerContext { + return TransformerContext(PLUGIN_CACHE_FILE, requireResourceAsStream(PLUGIN_CACHE_FILE), relocator.toList()) + } + + private companion object { + const val PLUGIN_CACHE_FILE = "META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat" + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt index c36db4cc0..e699711a7 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -8,7 +8,7 @@ import assertk.assertions.isNotEmpty import assertk.assertions.isTrue import org.junit.jupiter.api.Test -class ManifestAppenderTransformerTest : TransformerTestSupport() { +class ManifestAppenderTransformerTest : BaseTransformerTest() { @Test fun testCanTransformResource() { with(transformer) { @@ -16,8 +16,8 @@ class ManifestAppenderTransformerTest : TransformerTestSupport() { +class PropertiesFileTransformerTest : BaseTransformerTest() { @Test fun testHasTransformedResource() { transformer.transform(manifestTransformerContext) @@ -46,4 +54,282 @@ class PropertiesFileTransformerTest : TransformerTestSupport, + input2: Map, + expectedOutput: Map, + ) { + transformer.mergeStrategy.set(MergeStrategy.from(mergeStrategy)) + transformer.mergeSeparator.set(mergeSeparator) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + @ParameterizedTest(name = "Paths={1}") + @MethodSource("transformConfigurationsWithPathsProvider") + fun exerciseAllTransformConfigurationsWithPaths( + path: String, + paths: List, + input1: Map, + input2: Map, + expectedOutput: Map, + ) { + transformer.paths.set(paths) + transformer.mergeStrategy.set(MergeStrategy.First) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + @ParameterizedTest(name = "Mappings={1}") + @MethodSource("transformConfigurationsWithMappingsProvider") + fun exerciseAllTransformConfigurationsWithMappings( + path: String, + mappings: Map>, + input1: Map, + input2: Map, + expectedOutput: Map, + ) { + transformer.mappings.set(mappings) + transformer.mergeStrategy.set(MergeStrategy.Latest) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + @ParameterizedTest(name = "KeyTransformer: {1}") + @MethodSource("appliesKeyTransformerProvider") + fun appliesKeyTransformer( + path: String, + keyTransformer: (String) -> String, + input1: Map, + input2: Map, + expectedOutput: Map, + ) { + transformer.mergeStrategy.set(MergeStrategy.Append) + transformer.keyTransformer.set(object : Closure(null) { + override fun call(vararg arguments: Any?): String { + return keyTransformer.invoke(arguments.first() as String) + } + }) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + @ParameterizedTest(name = "Charset: {1}") + @MethodSource("appliesCharsetProvider") + fun appliesCharset( + path: String, + charset: String, + input: Map, + expectedOutput: Map, + ) { + transformer.charsetName.set(charset) + + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input, Charset.forName(charset))) + } + + assertThat(transformer.propertiesEntries[path].orEmpty()).isEqualTo(expectedOutput) + } + + private companion object { + fun context(path: String, input: Map, charset: Charset = Charsets.ISO_8859_1): TransformerContext { + val properties = Properties().apply { putAll(input) } + return TransformerContext(path, properties.inputStream(charset), stats = sharedStats) + } + + @JvmStatic + fun pathProvider() = listOf( + Arguments.of("foo.properties", true, "can be"), + Arguments.of("foo/bar.properties", true, "can be"), + Arguments.of("foo.props", false, "can not be"), + ) + + @JvmStatic + fun appliesCharsetProvider() = listOf( + Arguments.of( + "utf8.properties", + "utf-8", + mapOf("foo" to "传傳磨宿说説"), + mapOf("foo" to "传傳磨宿说説"), + ), + ) + + @JvmStatic + fun transformConfigurationsWithPathsProvider() = listOf( + Arguments.of( + "f.properties", + listOf("f.properties"), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "foo.properties", + listOf(".*.properties"), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "foo.properties", + listOf(".*bar"), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + emptyMap(), + ), + Arguments.of( + "foo.properties", + emptyList(), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + ) + + @JvmStatic + fun transformConfigurationsWithMappingsProvider() = listOf( + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "first")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "latest")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "bar"), + ), + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "append")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo,bar"), + ), + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "append", "mergeSeparator" to ";")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo;bar"), + ), + Arguments.of( + "foo.properties", + mapOf(".*.properties" to mapOf("mergeStrategy" to "first")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "foo.properties", + mapOf(".*bar" to mapOf("mergeStrategy" to "first")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + emptyMap(), + ), + ) + + @JvmStatic + fun transformConfigurationsProvider() = listOf( + Arguments.of( + "f.properties", + "first", + "", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "f.properties", + "latest", + "", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "bar"), + ), + Arguments.of( + "f.properties", + "append", + ",", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo,bar"), + ), + Arguments.of( + "f.properties", + "append", + ";", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo;bar"), + ), + ) + + @JvmStatic + fun appliesKeyTransformerProvider() = listOf( + Arguments.of( + "foo.properties", + { key: String -> key }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("foo" to "bar", "FOO" to "baz"), + ), + Arguments.of( + "foo.properties", + { key: String -> key.uppercase() }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("FOO" to "bar,baz"), + ), + Arguments.of( + "foo.properties", + { key: String -> "bar.${key.lowercase()}" }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("bar.foo" to "bar,baz"), + ), + Arguments.of( + "foo.properties", + { key: String -> key.replaceFirst(Regex("^(foo)"), "bar.$1") }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("bar.foo" to "bar", "FOO" to "baz"), + ), + ) + } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt new file mode 100644 index 000000000..55bec8828 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -0,0 +1,64 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +class ServiceFileTransformerTest : BaseTransformerTest() { + @ParameterizedTest(name = "{index} => path={0}, exclude={1}, expected={2}") + @MethodSource("canTransformResourceData") + fun testCanTransformResource(path: String, exclude: Boolean, expected: Boolean) { + if (exclude) { + transformer.exclude(path) + } + assertThat(transformer.canTransformResource(path)).isEqualTo(expected) + } + + @ParameterizedTest(name = "{index} => path={0}") + @MethodSource("transformsServiceFileData") + fun `test transforms service file`(path: String, input1: String, input2: String, output: String) { + if (transformer.canTransformResource(path)) { + transformer.transform(context(path, input1)) + transformer.transform(context(path, input2)) + } + + assertThat(transformer.hasTransformedResource()).isTrue() + assertThat(transformer.serviceEntries.getValue(path).toInputStream().bufferedReader().readText()) + .isEqualTo(output) + } + + @Test + fun `test excludes Groovy extension module descriptor files by default`() { + val element = "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" + assertThat(transformer.canTransformResource(element)).isFalse() + } + + private companion object { + fun context(path: String, input: String): TransformerContext { + return TransformerContext(path, input.byteInputStream(), stats = sharedStats) + } + + @JvmStatic + fun canTransformResourceData() = listOf( + // path, exclude, expected + Arguments.of("META-INF/services/java.sql.Driver", false, true), + Arguments.of("META-INF/services/io.dropwizard.logging.AppenderFactory", false, true), + Arguments.of("META-INF/services/org.apache.maven.Shade", true, false), + Arguments.of("META-INF/services/foo/bar/moo.goo.Zoo", false, true), + Arguments.of("foo/bar.properties", false, false), + Arguments.of("foo.props", false, false), + ) + + @JvmStatic + fun transformsServiceFileData() = listOf( + // path, input1, input2, output + Arguments.of("META-INF/services/com.acme.Foo", "foo", "bar", "foo\nbar"), + Arguments.of("META-INF/services/com.acme.Bar", "foo\nbar", "zoo", "foo\nbar\nzoo"), + ) + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index b9eddbf31..eddd1a2d0 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -5,7 +5,7 @@ import assertk.assertions.isFalse import assertk.assertions.isTrue import org.junit.jupiter.api.Test -class XmlAppendingTransformerTest : TransformerTestSupport() { +class XmlAppendingTransformerTest : BaseTransformerTest() { init { setupTurkishLocale() @@ -15,8 +15,8 @@ class XmlAppendingTransformerTest : TransformerTestSupport Date: Thu, 5 Dec 2024 09:28:13 +0800 Subject: [PATCH 073/941] Update dependency com.pinterest.ktlint:ktlint-cli to v1.5.0 (#1089) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4228ce9d8..5b57ad69c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,7 +13,7 @@ xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha02" assertk-lint = "com.jzbrooks:assertk-lint:1.3.0" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. -ktlint = "com.pinterest.ktlint:ktlint-cli:1.4.1" +ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" spock = "org.spockframework:spock-core:2.3-groovy-3.0" junit-bom = "org.junit:junit-bom:5.11.3" From eee7e8cb62de87c3f931affa7a177d643e75c497 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 6 Dec 2024 07:58:22 -0500 Subject: [PATCH 074/941] Adjust property initializations and modifiers in ShadowJar (#1090) * Init properties in ShadowJar * Tweak doc and test * Dump API * Tweak optional things * Tweak modifiers * Mark open * Update changelog * More open * Simplify * Update src/docs/changes/README.md --- api/shadow.api | 25 +++++---- src/docs/changes/README.md | 4 +- src/docs/custom-tasks/README.md | 3 - .../plugins/shadow/ShadowPluginSpec.groovy | 1 - .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 6 -- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 55 ++++++++++--------- 6 files changed, 43 insertions(+), 51 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 9b750800c..64656cd0d 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -306,21 +306,22 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar protected fun createCopyAction ()Lorg/gradle/api/internal/file/copy/CopyAction; public fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public final fun getApiJars ()Lorg/gradle/api/file/ConfigurableFileCollection; - public abstract fun getConfigurations ()Lorg/gradle/api/provider/ListProperty; - public abstract fun getDependencyFilter ()Lorg/gradle/api/provider/Property; - public abstract fun getEnableRelocation ()Lorg/gradle/api/provider/Property; - public abstract fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; + public fun getApiJars ()Lorg/gradle/api/file/ConfigurableFileCollection; + public fun getConfigurations ()Lorg/gradle/api/provider/ListProperty; + public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; + public fun getEnableRelocation ()Lorg/gradle/api/provider/Property; + public fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; + protected fun getInternalCompressor ()Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor; public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; - public abstract fun getMinimizeJar ()Lorg/gradle/api/provider/Property; - public abstract fun getRelocationPrefix ()Lorg/gradle/api/provider/Property; - public abstract fun getRelocators ()Lorg/gradle/api/provider/ListProperty; - public final fun getRootPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; - public final fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/ConfigurableFileCollection; + public fun getMinimizeJar ()Lorg/gradle/api/provider/Property; + public fun getRelocationPrefix ()Lorg/gradle/api/provider/Property; + public fun getRelocators ()Lorg/gradle/api/provider/ListProperty; + protected fun getRootPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; + public fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; - public final fun getToMinimize ()Lorg/gradle/api/file/ConfigurableFileCollection; - public abstract fun getTransformers ()Lorg/gradle/api/provider/ListProperty; + public fun getToMinimize ()Lorg/gradle/api/file/ConfigurableFileCollection; + public fun getTransformers ()Lorg/gradle/api/provider/ListProperty; public fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 5606e3a48..aa8336a29 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -5,8 +5,8 @@ **Fixed** -- Mark `enableRelocation`, `relocationPrefix`, and `minimizeJar` optional. ([#1074](https://github.com/GradleUp/shadow/pull/1074)) - This fix the regression for registering custom `ShadowJar` tasks. +- Adjust property initializations and modifiers in `ShadowJar`. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) + This fixes the regression for registering custom `ShadowJar` tasks. ## [v9.0.0-beta2] (2024-11-28) diff --git a/src/docs/custom-tasks/README.md b/src/docs/custom-tasks/README.md index f94890bcb..ccf85a5e5 100644 --- a/src/docs/custom-tasks/README.md +++ b/src/docs/custom-tasks/README.md @@ -14,9 +14,6 @@ def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle. archiveClassifier = "tests" from sourceSets.test.output configurations = [project.configurations.testRuntimeClasspath] - includedDependencies.setFrom(configurations) - - // May have to configure more properties here, you can ref https://github.com/GradleUp/shadow/blob/8ddb7c5334d15ef054f37aa9999e7680a55c5b2a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt#L94-L99 for more details. } // Optionally, make the `assemble` task depend on the new task diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index 3db47c414..f57b069fb 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -1259,7 +1259,6 @@ class ShadowPluginSpec extends PluginSpecification { archiveClassifier = "tests" from sourceSets.test.output configurations = [project.configurations.testRuntimeClasspath] - includedDependencies.setFrom(configurations) } """.stripIndent() 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 4b7d869bf..f688b1eec 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 @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import javax.inject.Inject @@ -91,12 +90,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( } } shadow.from(sourceSets.named("main").map { it.output }) - shadow.enableRelocation.convention(false) - shadow.minimizeJar.convention(false) - shadow.relocationPrefix.convention(ShadowBasePlugin.SHADOW) - shadow.dependencyFilter.convention(DefaultDependencyFilter(project)) shadow.configurations.convention(listOf(project.runtimeConfiguration)) - shadow.includedDependencies.setFrom(shadow.dependencyFilter.map { it.resolve(shadow.configurations.get()) }) shadow.exclude( "META-INF/INDEX.LIST", "META-INF/*.SF", diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 811330245..d75519067 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -2,11 +2,13 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.DefaultZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor +import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.unsafeLazy import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator @@ -66,18 +68,18 @@ public abstract class ShadowJar : override val stats: ShadowStats = ShadowStats() @get:Classpath - public val toMinimize: ConfigurableFileCollection by unsafeLazy { + public open val toMinimize: ConfigurableFileCollection by unsafeLazy { objectFactory.fileCollection().apply { - if (minimizeJar.getOrElse(false)) { + if (minimizeJar.get()) { setFrom(dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) } } } @get:Classpath - public val apiJars: ConfigurableFileCollection by unsafeLazy { + public open val apiJars: ConfigurableFileCollection by unsafeLazy { objectFactory.fileCollection().apply { - if (minimizeJar.getOrElse(false)) { + if (minimizeJar.get()) { setFrom(UnusedTracker.getApiJarsFromProject(project)) } } @@ -85,9 +87,9 @@ public abstract class ShadowJar : @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) - public val sourceSetsClassesDirs: ConfigurableFileCollection by unsafeLazy { + public open val sourceSetsClassesDirs: ConfigurableFileCollection by unsafeLazy { objectFactory.fileCollection().apply { - if (minimizeJar.getOrElse(false)) { + if (minimizeJar.get()) { project.extensions.getByType(SourceSetContainer::class.java).forEach { sourceSet -> from(sourceSet.output.classesDirs.filter { it.isDirectory }) } @@ -95,15 +97,12 @@ public abstract class ShadowJar : } } - @get:Classpath - public abstract val includedDependencies: ConfigurableFileCollection - @get:Internal - public val rootPatternSet: PatternSet + protected open val rootPatternSet: PatternSet get() = (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet @get:Internal - internal val internalCompressor: ZipCompressor + protected open val internalCompressor: ZipCompressor get() { return when (entryCompression) { ZipEntryCompression.DEFLATED -> DefaultZipCompressor(isZip64, ZipOutputStream.DEFLATED) @@ -113,26 +112,30 @@ public abstract class ShadowJar : } @get:Nested - public abstract val transformers: ListProperty + public open val transformers: ListProperty = objectFactory.listProperty(Transformer::class.java) @get:Nested - public abstract val relocators: ListProperty + public open val relocators: ListProperty = objectFactory.listProperty(Relocator::class.java) @get:Classpath @get:Optional - public abstract val configurations: ListProperty + public open val configurations: ListProperty = objectFactory.listProperty(Configuration::class.java) @get:Internal - public abstract val dependencyFilter: Property + public open val dependencyFilter: Property = + objectFactory.property(DefaultDependencyFilter(project)) + + @get:Classpath + public open val includedDependencies: ConfigurableFileCollection = objectFactory.fileCollection() + .apply { setFrom(dependencyFilter.map { it.resolve(configurations.get()) }) } /** * Enable relocation of packages in the jar. * - * Defaults to false. + * Defaults to `false`. */ @get:Input - @get:Optional - public abstract val enableRelocation: Property + public open val enableRelocation: Property = objectFactory.property(false) /** * Prefix to use for relocated packages. @@ -140,17 +143,15 @@ public abstract class ShadowJar : * Defaults to [ShadowBasePlugin.SHADOW]. */ @get:Input - @get:Optional - public abstract val relocationPrefix: Property + public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) /** * Minimize the jar by removing unused classes. * - * Defaults to false. + * Defaults to `false`. */ @get:Input - @get:Optional - public abstract val minimizeJar: Property + public open val minimizeJar: Property = objectFactory.property(false) @Internal override fun getManifest(): InheritManifest = super.manifest as InheritManifest @@ -249,7 +250,7 @@ public abstract class ShadowJar : override fun createCopyAction(): CopyAction { val documentationRegistry = services.get(DocumentationRegistry::class.java) - val unusedTracker = if (minimizeJar.getOrElse(false)) { + val unusedTracker = if (minimizeJar.get()) { UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) } else { null @@ -264,7 +265,7 @@ public abstract class ShadowJar : rootPatternSet, stats, isPreserveFileTimestamps, - minimizeJar.getOrElse(false), + minimizeJar.get(), unusedTracker, ) } @@ -289,9 +290,9 @@ public abstract class ShadowJar : private val packageRelocators: List get() { - if (!enableRelocation.getOrElse(false)) return emptyList() + if (!enableRelocation.get()) return emptyList() - val prefix = relocationPrefix.getOrElse(ShadowBasePlugin.SHADOW) + val prefix = relocationPrefix.get() // Must cast configurations to List to fix type mismatch in runtime. return (configurations.get() as List).flatMap { configuration -> configuration.files.flatMap { file -> From 75c73f8ba84f662bba1faa13bf21113a8ad46aba Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 6 Dec 2024 08:11:21 -0500 Subject: [PATCH 075/941] Use compat APIs for Gradle (#1091) --- .../plugins/shadow/internal/GradleCompat.kt | 36 +++++++++++++++++++ .../gradle/plugins/shadow/internal/Utils.kt | 19 ---------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 7 ++-- 3 files changed, 40 insertions(+), 22 deletions(-) create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt new file mode 100644 index 000000000..a4a3b5dae --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -0,0 +1,36 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.provider.Property +import org.gradle.util.GradleVersion + +/** + * Return `runtimeClasspath` or `runtime` configuration. + */ +internal inline val Project.runtimeConfiguration: Configuration + get() { + return configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) + ?: configurations.getByName("runtime") + } + +internal inline fun ObjectFactory.property(defaultValue: T? = null): Property { + return property(T::class.java).apply { + if (defaultValue != null) convention(defaultValue) + } +} + +/** + * TODO: this could be removed after bumping the min Gradle requirement to 8.8 or above. + */ +internal fun ConfigurableFileCollection.conventionCompat(vararg paths: Any): ConfigurableFileCollection { + return if (GradleVersion.current() >= GradleVersion.version("8.8")) { + convention(paths) + } else { + setFrom(paths) + this + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 61d774cd6..4ec58607b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -7,31 +7,12 @@ import java.io.FileNotFoundException import java.io.InputStream import java.nio.charset.Charset import java.util.Properties -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration import org.gradle.api.file.RelativePath import org.gradle.api.internal.file.DefaultFileTreeElement -import org.gradle.api.model.ObjectFactory -import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.provider.Property import org.gradle.internal.file.Chmod import org.gradle.internal.file.FileMetadata import org.gradle.internal.file.Stat -/** - * Return `runtimeClasspath` or `runtime` configuration. - */ -internal inline val Project.runtimeConfiguration: Configuration get() { - return configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) - ?: configurations.getByName("runtime") -} - -internal inline fun ObjectFactory.property(defaultValue: T? = null): Property { - return property(T::class.java).apply { - if (defaultValue != null) convention(defaultValue) - } -} - /** * This is used for creating a [DefaultFileTreeElement] with default values. * [file], [chmod], and [stat] should be non-null, so they are set to dummy values here. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index d75519067..c9e84ed21 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -8,6 +8,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor +import com.github.jengelman.gradle.plugins.shadow.internal.conventionCompat import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.unsafeLazy import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator @@ -71,7 +72,7 @@ public abstract class ShadowJar : public open val toMinimize: ConfigurableFileCollection by unsafeLazy { objectFactory.fileCollection().apply { if (minimizeJar.get()) { - setFrom(dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) + conventionCompat(dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) } } } @@ -80,7 +81,7 @@ public abstract class ShadowJar : public open val apiJars: ConfigurableFileCollection by unsafeLazy { objectFactory.fileCollection().apply { if (minimizeJar.get()) { - setFrom(UnusedTracker.getApiJarsFromProject(project)) + conventionCompat(UnusedTracker.getApiJarsFromProject(project)) } } } @@ -127,7 +128,7 @@ public abstract class ShadowJar : @get:Classpath public open val includedDependencies: ConfigurableFileCollection = objectFactory.fileCollection() - .apply { setFrom(dependencyFilter.map { it.resolve(configurations.get()) }) } + .conventionCompat(dependencyFilter.map { it.resolve(configurations.get()) }) /** * Enable relocation of packages in the jar. From 11534ca3fe8b44a3246cc2552bd67755a28747dc Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 6 Dec 2024 08:20:42 -0500 Subject: [PATCH 076/941] Only expose includes and excludes as inputs in SimpleRelocator (#1079) * Remove public properties * Migrate includes and excludes to using ListProperty * Note this change --- api/shadow.api | 23 ++-- src/docs/changes/README.md | 4 + .../shadow/relocation/SimpleRelocator.kt | 107 ++++++++---------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 4 +- .../shadow/relocation/SimpleRelocatorTest.kt | 9 +- .../Log4j2PluginsCacheFileTransformerTest.kt | 1 + .../gradle/plugins/shadow/util/Utils.kt | 17 +++ 7 files changed, 83 insertions(+), 82 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 64656cd0d..1aefba242 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -185,24 +185,19 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat } public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator : com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { - public fun ()V - public fun (Ljava/lang/String;)V - public fun (Ljava/lang/String;Ljava/lang/String;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V - public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lorg/gradle/api/model/ObjectFactory;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V + public synthetic fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; public fun canRelocateClass (Ljava/lang/String;)Z public fun canRelocatePath (Ljava/lang/String;)Z public fun exclude (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; - public fun getExcludes ()Ljava/util/Set; - public fun getIncludes ()Ljava/util/Set; - public fun getPathPattern ()Ljava/lang/String; - public fun getPattern ()Ljava/lang/String; - public fun getRawString ()Z - public fun getShadedPathPattern ()Ljava/lang/String; - public fun getShadedPattern ()Ljava/lang/String; + public final fun getExcludes ()Lorg/gradle/api/provider/ListProperty; + public final fun getIncludes ()Lorg/gradle/api/provider/ListProperty; public fun include (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index aa8336a29..2be1b6abd 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Changed** + +- **BREAKING CHANGE:** Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as `ListProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) + **Fixed** - Adjust property initializations and modifiers in `ShadowJar`. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 7ec2b8c3e..327a80310 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -2,8 +2,9 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import java.util.regex.Pattern import org.codehaus.plexus.util.SelectorUtils +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional /** * Modified from [org.apache.maven.plugins.shade.relocation.SimpleRelocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java). @@ -14,80 +15,62 @@ import org.gradle.api.tasks.Optional */ @CacheableRelocator public open class SimpleRelocator @JvmOverloads constructor( + objectFactory: ObjectFactory, pattern: String? = null, shadedPattern: String? = null, includes: List? = null, excludes: List? = null, - private val _rawString: Boolean = false, + private val rawString: Boolean = false, ) : Relocator { - private val _pattern: String - private val _pathPattern: String - private val _shadedPattern: String - private val _shadedPathPattern: String - private val _includes = mutableSetOf() - private val _excludes = mutableSetOf() + private val pattern: String + private val pathPattern: String + private val shadedPattern: String + private val shadedPathPattern: String + + @get:Input + public val includes: ListProperty = objectFactory.listProperty(String::class.java) + + @get:Input + public val excludes: ListProperty = objectFactory.listProperty(String::class.java) init { - if (_rawString) { - _pathPattern = pattern.orEmpty() - _shadedPathPattern = shadedPattern.orEmpty() - _pattern = "" // not used for raw string relocator - _shadedPattern = "" // not used for raw string relocator + if (rawString) { + this.pathPattern = pattern.orEmpty() + this.shadedPathPattern = shadedPattern.orEmpty() + this.pattern = "" // not used for raw string relocator + this.shadedPattern = "" // not used for raw string relocator } else { if (pattern == null) { - _pattern = "" - _pathPattern = "" + this.pattern = "" + this.pathPattern = "" } else { - _pattern = pattern.replace('/', '.') - _pathPattern = pattern.replace('.', '/') + this.pattern = pattern.replace('/', '.') + this.pathPattern = pattern.replace('.', '/') } if (shadedPattern == null) { - _shadedPattern = "hidden.${_pattern}" - _shadedPathPattern = "hidden/$_pathPattern" + this.shadedPattern = "hidden.${this.pattern}" + this.shadedPathPattern = "hidden/${this.pathPattern}" } else { - _shadedPattern = shadedPattern.replace('/', '.') - _shadedPathPattern = shadedPattern.replace('.', '/') + this.shadedPattern = shadedPattern.replace('/', '.') + this.shadedPathPattern = shadedPattern.replace('.', '/') } } - _includes += normalizePatterns(includes) - _excludes += normalizePatterns(excludes) + this.includes.addAll(normalizePatterns(includes)) + this.excludes.addAll(normalizePatterns(excludes)) } - @get:Input - @get:Optional - public open val pattern: String get() = _pattern - - @get:Input - public open val pathPattern: String get() = _pathPattern - - @get:Input - @get:Optional - public open val shadedPattern: String get() = _shadedPattern - - @get:Input - public open val shadedPathPattern: String get() = _shadedPathPattern - - @get:Input - public open val rawString: Boolean get() = _rawString - - @get:Input - public open val includes: Set get() = _includes - - @get:Input - public open val excludes: Set get() = _excludes - public open fun include(pattern: String): SimpleRelocator = apply { - _includes += normalizePatterns(listOf(pattern)) + includes.addAll(normalizePatterns(listOf(pattern))) } public open fun exclude(pattern: String): SimpleRelocator = apply { - _excludes += normalizePatterns(listOf(pattern)) + excludes.addAll(normalizePatterns(listOf(pattern))) } override fun canRelocatePath(path: String): Boolean { - if (_rawString) return Pattern.compile(_pathPattern).matcher(path).find() + if (rawString) return Pattern.compile(pathPattern).matcher(path).find() // If string is too short - no need to perform expensive string operations - if (path.length < _pathPattern.length) return false + if (path.length < pathPattern.length) return false val adjustedPath = if (path.endsWith(".class")) { // Safeguard against strings containing only ".class" if (path.length == 6) return false @@ -97,43 +80,43 @@ public open class SimpleRelocator @JvmOverloads constructor( } // Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties"). val startIndex = if (adjustedPath.startsWith("/")) 1 else 0 - val pathStartsWithPattern = adjustedPath.startsWith(_pathPattern, startIndex) + val pathStartsWithPattern = adjustedPath.startsWith(pathPattern, startIndex) return pathStartsWithPattern && isIncluded(adjustedPath) && !isExcluded(adjustedPath) } override fun canRelocateClass(className: String): Boolean { - return !_rawString && !className.contains('/') && canRelocatePath(className.replace('.', '/')) + return !rawString && !className.contains('/') && canRelocatePath(className.replace('.', '/')) } override fun relocatePath(context: RelocatePathContext): String { val path = context.path - context.stats.relocate(_pathPattern, _shadedPathPattern) - return if (_rawString) { - path.replace(_pathPattern.toRegex(), _shadedPathPattern) + context.stats.relocate(pathPattern, shadedPathPattern) + return if (rawString) { + path.replace(pathPattern.toRegex(), shadedPathPattern) } else { - path.replaceFirst(_pathPattern, _shadedPathPattern) + path.replaceFirst(pathPattern, shadedPathPattern) } } override fun relocateClass(context: RelocateClassContext): String { - context.stats.relocate(_pathPattern, _shadedPathPattern) - return context.className.replaceFirst(_pattern, _shadedPattern) + context.stats.relocate(pathPattern, shadedPathPattern) + return context.className.replaceFirst(pattern, shadedPattern) } override fun applyToSourceContent(sourceContent: String): String { - return if (_rawString) { + return if (rawString) { sourceContent } else { - sourceContent.replace("\\b$_pattern".toRegex(), _shadedPattern) + sourceContent.replace("\\b$pattern".toRegex(), shadedPattern) } } private fun isIncluded(path: String): Boolean { - return _includes.isEmpty() || _includes.any { SelectorUtils.matchPath(it, path, "/", true) } + return includes.get().isEmpty() || includes.get().any { SelectorUtils.matchPath(it, path, "/", true) } } private fun isExcluded(path: String): Boolean { - return _excludes.any { SelectorUtils.matchPath(it, path, "/", true) } + return excludes.get().any { SelectorUtils.matchPath(it, path, "/", true) } } private companion object { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index c9e84ed21..8640650a5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -225,7 +225,7 @@ public abstract class ShadowJar : destination: String, action: Action?, ): ShadowJar = apply { - val relocator = SimpleRelocator(pattern, destination) + val relocator = SimpleRelocator(objectFactory, pattern, destination) addRelocator(relocator, action) } @@ -301,7 +301,7 @@ public abstract class ShadowJar : jarFile.entries().toList() .filter { it.name.endsWith(".class") && it.name != "module-info.class" } .map { it.name.substringBeforeLast('/').replace('/', '.') } - .map { SimpleRelocator(it, "$prefix.$it") } + .map { SimpleRelocator(objectFactory, it, "$prefix.$it") } } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index b5c9a070e..4b13cd5ba 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -4,6 +4,7 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.util.SimpleRelocator import org.junit.jupiter.api.Test /** @@ -132,10 +133,10 @@ class SimpleRelocatorTest { @Test fun testCanRelocateRawString() { - var relocator = SimpleRelocator("org/foo", _rawString = true) + var relocator = SimpleRelocator("org/foo", rawString = true) assertThat(relocator.canRelocatePath("(I)org/foo/bar/Class")).isTrue() - relocator = SimpleRelocator("^META-INF/org.foo.xml\$", _rawString = true) + relocator = SimpleRelocator("^META-INF/org.foo.xml\$", rawString = true) assertThat(relocator.canRelocatePath("META-INF/org.foo.xml")).isTrue() } @@ -170,11 +171,11 @@ class SimpleRelocatorTest { @Test fun testRelocateRawString() { - var relocator = SimpleRelocator("Lorg/foo", "Lhidden/org/foo", _rawString = true) + var relocator = SimpleRelocator("Lorg/foo", "Lhidden/org/foo", rawString = true) assertThat(relocator.relocatePath("(I)Lorg/foo/bar/Class")) .isEqualTo("(I)Lhidden/org/foo/bar/Class") - relocator = SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", _rawString = true) + relocator = SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", rawString = true) assertThat(relocator.relocatePath("META-INF/org.foo.xml")) .isEqualTo("META-INF/hidden.org.foo.xml") } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index 48a4b1ff3..1b933940d 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -4,6 +4,7 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isTrue import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.util.SimpleRelocator import java.io.File import java.net.URL import java.util.Collections diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt index a6acfd481..5f8b31152 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt @@ -1,6 +1,23 @@ package com.github.jengelman.gradle.plugins.shadow.util +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import org.gradle.api.model.ObjectFactory import org.gradle.testfixtures.ProjectBuilder val testObjectFactory: ObjectFactory = ProjectBuilder.builder().build().objects + +@Suppress("TestFunctionName") +fun SimpleRelocator( + pattern: String? = null, + shadedPattern: String? = null, + includes: List? = null, + excludes: List? = null, + rawString: Boolean = false, +): SimpleRelocator = SimpleRelocator( + objectFactory = testObjectFactory, + pattern = pattern, + shadedPattern = shadedPattern, + includes = includes, + excludes = excludes, + rawString = rawString, +) From 75b17eadcbe7c0741762ba51f20200f5bf787e35 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 6 Dec 2024 21:24:58 +0800 Subject: [PATCH 077/941] Prepare version 9.0.0-beta3 --- gradle.properties | 2 +- src/docs/changes/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index c64924434..c127ac5b9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.configuration-cache.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta3 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 2be1b6abd..2501e53dd 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased] + +## [v9.0.0-beta3] (2024-12-06) + **Changed** - **BREAKING CHANGE:** Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as `ListProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) @@ -456,7 +459,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Fri, 6 Dec 2024 21:25:13 +0800 Subject: [PATCH 078/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c127ac5b9..c64924434 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.configuration-cache.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta3 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From ce00c6827b13de592cd0ffed35696ae6faf95142 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 6 Dec 2024 21:37:27 +0800 Subject: [PATCH 079/941] Update release task group --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index aa4265751..d0826c9c5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -142,7 +142,7 @@ tasks.whenTaskAdded { tasks.register("release") { description = "Publishes the plugin to maven repos and deploys website." - group = LifecycleBasePlugin.VERIFICATION_GROUP + group = PublishingPlugin.PUBLISH_TASK_GROUP dependsOn( tasks.publish, From 4d0889d5745ae130949b0a2973a8e371a4d162b8 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 6 Dec 2024 09:55:15 -0500 Subject: [PATCH 080/941] Use SetProperty on includes and excludes (#1092) --- api/shadow.api | 4 ++-- src/docs/changes/README.md | 4 ++-- .../gradle/plugins/shadow/relocation/SimpleRelocator.kt | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 1aefba242..c73a61e14 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -196,8 +196,8 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public fun canRelocateClass (Ljava/lang/String;)Z public fun canRelocatePath (Ljava/lang/String;)Z public fun exclude (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; - public final fun getExcludes ()Lorg/gradle/api/provider/ListProperty; - public final fun getIncludes ()Lorg/gradle/api/provider/ListProperty; + public final fun getExcludes ()Lorg/gradle/api/provider/SetProperty; + public final fun getIncludes ()Lorg/gradle/api/provider/SetProperty; public fun include (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 2501e53dd..c3f88ef81 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -4,11 +4,11 @@ ## [Unreleased] -## [v9.0.0-beta3] (2024-12-06) +## [v9.0.0-beta] (2024-12-06) **Changed** -- **BREAKING CHANGE:** Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as `ListProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) +- **BREAKING CHANGE:** Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as `SetProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) **Fixed** diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 327a80310..1fa662d35 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -3,7 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import java.util.regex.Pattern import org.codehaus.plexus.util.SelectorUtils import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input /** @@ -28,10 +28,10 @@ public open class SimpleRelocator @JvmOverloads constructor( private val shadedPathPattern: String @get:Input - public val includes: ListProperty = objectFactory.listProperty(String::class.java) + public val includes: SetProperty = objectFactory.setProperty(String::class.java) @get:Input - public val excludes: ListProperty = objectFactory.listProperty(String::class.java) + public val excludes: SetProperty = objectFactory.setProperty(String::class.java) init { if (rawString) { From 8d0b2cb2d652efa601d0bb4c079fefa7cbc59bfe Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 6 Dec 2024 22:56:53 +0800 Subject: [PATCH 081/941] Prepare version 9.0.0-beta4 --- gradle.properties | 2 +- src/docs/changes/README.md | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/gradle.properties b/gradle.properties index c64924434..8b74235aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.configuration-cache.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta4 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index c3f88ef81..8e1dabf32 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -4,7 +4,7 @@ ## [Unreleased] -## [v9.0.0-beta] (2024-12-06) +## [v9.0.0-beta4] (2024-12-06) **Changed** @@ -459,7 +459,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Fri, 6 Dec 2024 21:25:13 +0800 Subject: [PATCH 082/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 8b74235aa..c64924434 100644 --- a/gradle.properties +++ b/gradle.properties @@ -7,7 +7,7 @@ org.gradle.configuration-cache.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta4 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 284970302df7b798714d6a495dcd3eedfaf3a68c Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 7 Dec 2024 10:14:17 +0800 Subject: [PATCH 083/941] Clean up SimpleRelocator --- .../plugins/shadow/relocation/SimpleRelocator.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 1fa662d35..feb002388 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -47,12 +47,12 @@ public open class SimpleRelocator @JvmOverloads constructor( this.pattern = pattern.replace('/', '.') this.pathPattern = pattern.replace('.', '/') } - if (shadedPattern == null) { - this.shadedPattern = "hidden.${this.pattern}" - this.shadedPathPattern = "hidden/${this.pathPattern}" - } else { + if (shadedPattern != null) { this.shadedPattern = shadedPattern.replace('/', '.') this.shadedPathPattern = shadedPattern.replace('.', '/') + } else { + this.shadedPattern = "hidden.${this.pattern}" + this.shadedPathPattern = "hidden/${this.pathPattern}" } } this.includes.addAll(normalizePatterns(includes)) @@ -78,10 +78,10 @@ public open class SimpleRelocator @JvmOverloads constructor( } else { path } - // Allow for annoying option of an extra / on the front of a path. See MSHADE-119 comes from getClass().getResource("/a/b/c.properties"). + // Allow for annoying option of an extra / on the front of a path. See MSHADE-119; + // comes from getClass().getResource("/a/b/c.properties"). val startIndex = if (adjustedPath.startsWith("/")) 1 else 0 - val pathStartsWithPattern = adjustedPath.startsWith(pathPattern, startIndex) - return pathStartsWithPattern && isIncluded(adjustedPath) && !isExcluded(adjustedPath) + return isIncluded(adjustedPath) && !isExcluded(adjustedPath) && adjustedPath.startsWith(pathPattern, startIndex) } override fun canRelocateClass(className: String): Boolean { @@ -94,7 +94,7 @@ public open class SimpleRelocator @JvmOverloads constructor( return if (rawString) { path.replace(pathPattern.toRegex(), shadedPathPattern) } else { - path.replaceFirst(pathPattern, shadedPathPattern) + path.replaceFirst(pathPattern.toRegex(), shadedPathPattern) } } From ccc04a67c791134114c64ef296b753d7ecbf8c45 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 7 Dec 2024 11:12:57 -0500 Subject: [PATCH 084/941] Tweak Gradle flags (#1093) * Omit automatic compile dependency on kotlin-stdlib * Enable allWarningsAsErrors * Rearrange * Depend on kotlin-stdlib * Revert "Depend on kotlin-stdlib" This reverts commit 7afb4d7992a7a82fc62527a68c09ba953a7689e2. * Depend on org.jetbrains:annotations * Revert "Depend on org.jetbrains:annotations" This reverts commit 57bb50f25290df3db71e5ec6d46ac145593eb305. * Replace NotNull with Nonnull --- build-logic/gradle.properties | 1 + gradle.properties | 10 +++++++--- src/docs/configuration/merging/README.md | 24 ++++++++++++------------ 3 files changed, 20 insertions(+), 15 deletions(-) create mode 100644 build-logic/gradle.properties diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties new file mode 100644 index 000000000..338c1c3fc --- /dev/null +++ b/build-logic/gradle.properties @@ -0,0 +1 @@ +org.gradle.kotlin.dsl.allWarningsAsErrors=true diff --git a/gradle.properties b/gradle.properties index c64924434..c67807733 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,9 +1,13 @@ -org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g -XX:MaxMetaspaceSize=2g -org.gradle.parallel=true +# Omit automatic compile dependency on kotlin-stdlib +# https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library +kotlin.stdlib.default.dependency=false + org.gradle.caching=true org.gradle.configuration-cache=true org.gradle.configuration-cache.parallel=true - +org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g -XX:MaxMetaspaceSize=2g +org.gradle.kotlin.dsl.allWarningsAsErrors=true +org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin diff --git a/src/docs/configuration/merging/README.md b/src/docs/configuration/merging/README.md index 2b6a6051c..9c9fbcc42 100644 --- a/src/docs/configuration/merging/README.md +++ b/src/docs/configuration/merging/README.md @@ -12,22 +12,22 @@ determine if it should process a particular entry and apply any modifications be // Adding a Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext +import javax.annotation.Nonnull import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement -import org.jetbrains.annotations.NotNull class MyTransformer implements Transformer { @Override - boolean canTransformResource(@NotNull FileTreeElement element) { return true } + boolean canTransformResource(@Nonnull FileTreeElement element) { return true } @Override - void transform(@NotNull TransformerContext context) {} + void transform(@Nonnull TransformerContext context) {} @Override boolean hasTransformedResource() { return true } @Override - void modifyOutputStream(@NotNull ZipOutputStream os, boolean preserveFileTimestamps) {} + void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -41,24 +41,24 @@ Additionally, a `Transformer` can accept a `Closure` to configure the provided ` // Configuring a Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext +import javax.annotation.Nonnull import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement -import org.jetbrains.annotations.NotNull class MyTransformer implements Transformer { boolean enabled @Override - boolean canTransformResource(@NotNull FileTreeElement element) { return true } + boolean canTransformResource(@Nonnull FileTreeElement element) { return true } @Override - void transform(@NotNull TransformerContext context) {} + void transform(@Nonnull TransformerContext context) {} @Override boolean hasTransformedResource() { return true } @Override - void modifyOutputStream(@NotNull ZipOutputStream os, boolean preserveFileTimestamps) {} + void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -74,9 +74,9 @@ An instantiated instance of a `Transformer` can also be provided. // Adding a Transformer Instance import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext +import javax.annotation.Nonnull import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement -import org.jetbrains.annotations.NotNull class MyTransformer implements Transformer { final boolean enabled @@ -86,16 +86,16 @@ class MyTransformer implements Transformer { } @Override - boolean canTransformResource(@NotNull FileTreeElement element) { return true } + boolean canTransformResource(@Nonnull FileTreeElement element) { return true } @Override - void transform(@NotNull TransformerContext context) {} + void transform(@Nonnull TransformerContext context) {} @Override boolean hasTransformedResource() { return true } @Override - void modifyOutputStream(@NotNull ZipOutputStream os, boolean preserveFileTimestamps) {} + void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { From bd3f02d5897ad8f4fc50ee8f1e3d2c160f76e01a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 7 Dec 2024 12:28:08 -0500 Subject: [PATCH 085/941] Sync SimpleRelocator changes from maven-shade-plugin (#1076) * Copy SimpleRelocatorTestNew.java from maven-shade-plugin Updated to https://github.com/apache/maven-shade-plugin/blob/f15f26b1b8100d0c3bfa15b9e8b233922432ee15/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java. * Convert SimpleRelocatorTestNew to Kotlin * Copy SimpleRelocatorNew.java from maven-shade-plugin https://github.com/Goooler/maven-shade-plugin/blob/49f6e13de5748700ef2185795f89f66be61554ef/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java * Convert SimpleRelocatorNew to Kotlin * Merge SimpleRelocatorTestNew into SimpleRelocatorTest * Merge SimpleRelocatorNew into SimpleRelocator * Expose sourcePackageExcludes and sourcePathExcludes * Revert "Expose sourcePackageExcludes and sourcePathExcludes" This reverts commit 2f2e3d5f9efb1a6b105983e3ce40783eab729c64. * Fix normalizePatterns * Note this change * Fix canRelocatePath --- src/docs/changes/README.md | 4 + .../shadow/relocation/SimpleRelocator.kt | 116 +++++++++++-- .../shadow/relocation/SimpleRelocatorTest.kt | 155 +++++++++++++++++- .../gradle/plugins/shadow/util/Utils.kt | 4 +- 4 files changed, 263 insertions(+), 16 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 8e1dabf32..63dc6b7f8 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Added** + +- Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) + ## [v9.0.0-beta4] (2024-12-06) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index feb002388..74d65b0fd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -26,6 +26,8 @@ public open class SimpleRelocator @JvmOverloads constructor( private val pathPattern: String private val shadedPattern: String private val shadedPathPattern: String + private val sourcePackageExcludes = mutableSetOf() + private val sourcePathExcludes = mutableSetOf() @get:Input public val includes: SetProperty = objectFactory.setProperty(String::class.java) @@ -57,6 +59,32 @@ public open class SimpleRelocator @JvmOverloads constructor( } this.includes.addAll(normalizePatterns(includes)) this.excludes.addAll(normalizePatterns(excludes)) + + // Don't replace all dots to slashes, otherwise /META-INF/maven/${groupId} can't be matched. + if (!includes.isNullOrEmpty()) { + this.includes.addAll(includes) + } + if (!excludes.isNullOrEmpty()) { + this.excludes.addAll(excludes) + } + + if (!rawString) { + // Create exclude pattern sets for sources + for (exclude in this.excludes.get()) { + // Excludes should be subpackages of the global pattern + if (exclude.startsWith(this.pattern)) { + sourcePackageExcludes.add( + exclude.substring(this.pattern.length).replaceFirst("[.][*]$".toRegex(), ""), + ) + } + // Excludes should be subpackages of the global pattern + if (exclude.startsWith(pathPattern)) { + sourcePathExcludes.add( + exclude.substring(pathPattern.length).replaceFirst("/[*]$".toRegex(), ""), + ) + } + } + } } public open fun include(pattern: String): SimpleRelocator = apply { @@ -71,7 +99,7 @@ public open class SimpleRelocator @JvmOverloads constructor( if (rawString) return Pattern.compile(pathPattern).matcher(path).find() // If string is too short - no need to perform expensive string operations if (path.length < pathPattern.length) return false - val adjustedPath = if (path.endsWith(".class")) { + var adjustedPath = if (path.endsWith(".class")) { // Safeguard against strings containing only ".class" if (path.length == 6) return false path.dropLast(6) @@ -80,8 +108,8 @@ public open class SimpleRelocator @JvmOverloads constructor( } // Allow for annoying option of an extra / on the front of a path. See MSHADE-119; // comes from getClass().getResource("/a/b/c.properties"). - val startIndex = if (adjustedPath.startsWith("/")) 1 else 0 - return isIncluded(adjustedPath) && !isExcluded(adjustedPath) && adjustedPath.startsWith(pathPattern, startIndex) + adjustedPath = adjustedPath.removePrefix("/") + return isIncluded(adjustedPath) && !isExcluded(adjustedPath) && adjustedPath.startsWith(pathPattern) } override fun canRelocateClass(className: String): Boolean { @@ -100,15 +128,17 @@ public open class SimpleRelocator @JvmOverloads constructor( override fun relocateClass(context: RelocateClassContext): String { context.stats.relocate(pathPattern, shadedPathPattern) - return context.className.replaceFirst(pattern, shadedPattern) + val clazz = context.className + return if (rawString) clazz else clazz.replaceFirst(pattern.toRegex(), shadedPattern) } + /** + * We don't call this function now, so we don't have to expose [sourcePackageExcludes] and [sourcePathExcludes] as inputs. + */ override fun applyToSourceContent(sourceContent: String): String { - return if (rawString) { - sourceContent - } else { - sourceContent.replace("\\b$pattern".toRegex(), shadedPattern) - } + if (rawString) return sourceContent + val content = shadeSourceWithExcludes(sourceContent, pattern, shadedPattern, sourcePackageExcludes) + return shadeSourceWithExcludes(content, pathPattern, shadedPathPattern, sourcePathExcludes) } private fun isIncluded(path: String): Boolean { @@ -120,6 +150,28 @@ public open class SimpleRelocator @JvmOverloads constructor( } private companion object { + /** + * Match dot, slash or space at end of string + */ + val RX_ENDS_WITH_DOT_SLASH_SPACE: Pattern = Pattern.compile("[./ ]$") + + /** + * Match + * - certain Java keywords + space + * - beginning of Javadoc link + optional line breaks and continuations with '*' + * - (opening curly brace / opening parenthesis / comma / equals / semicolon) + space + * - (closing curly brace / closing multi-line comment) + space + * + * at end of string + */ + val RX_ENDS_WITH_JAVA_KEYWORD: Pattern = Pattern.compile( + "\\b(import|package|public|protected|private|static|final|synchronized|abstract|volatile|extends|implements|throws) $" + + "|" + + "\\{@link( \\*)* $" + + "|" + + "([{}(=;,]|\\*/) $", + ) + fun normalizePatterns(patterns: Collection?) = buildSet { patterns ?: return@buildSet for (pattern in patterns) { @@ -131,12 +183,54 @@ public open class SimpleRelocator @JvmOverloads constructor( val classPattern = pattern.replace('.', '/') add(classPattern) - - if (classPattern.endsWith("/*")) { + // Actually, class patterns should just use 'foo.bar.*' ending with a single asterisk, but some users + // mistake them for path patterns like 'my/path/**', so let us be a bit more lenient here. + if (classPattern.endsWith("/*") || classPattern.endsWith("/**")) { val packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) add(packagePattern) } } } + + fun shadeSourceWithExcludes( + sourceContent: String, + patternFrom: String, + patternTo: String, + excludedPatterns: Set, + ): String { + // Usually shading makes package names a bit longer, so make buffer 10% bigger than original source + val shadedSourceContent = StringBuilder(sourceContent.length * 11 / 10) + var isFirstSnippet = true + // Make sure that search pattern starts at word boundary and that we look for literal ".", not regex jokers + val snippets = sourceContent.split(("\\b" + patternFrom.replace(".", "[.]") + "\\b").toRegex()) + .dropLastWhile { it.isEmpty() }.toTypedArray() + var i = 0 + val snippetsLength = snippets.size + while (i < snippetsLength) { + val snippet = snippets[i] + val previousSnippet = if (isFirstSnippet) "" else snippets[i - 1] + var doExclude = false + for (excludedPattern in excludedPatterns) { + if (snippet.startsWith(excludedPattern)) { + doExclude = true + break + } + } + if (isFirstSnippet) { + shadedSourceContent.append(snippet) + isFirstSnippet = false + } else { + val previousSnippetOneLine = previousSnippet.replace("\\s+".toRegex(), " ") + val afterDotSlashSpace = RX_ENDS_WITH_DOT_SLASH_SPACE.matcher(previousSnippetOneLine).find() + val afterJavaKeyWord = RX_ENDS_WITH_JAVA_KEYWORD.matcher(previousSnippetOneLine).find() + val shouldExclude = doExclude || afterDotSlashSpace && !afterJavaKeyWord + shadedSourceContent + .append(if (shouldExclude) patternFrom else patternTo) + .append(snippet) + } + i++ + } + return shadedSourceContent.toString() + } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 4b13cd5ba..0e9861b31 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -134,7 +134,7 @@ class SimpleRelocatorTest { @Test fun testCanRelocateRawString() { var relocator = SimpleRelocator("org/foo", rawString = true) - assertThat(relocator.canRelocatePath("(I)org/foo/bar/Class")).isTrue() + assertThat(relocator.canRelocatePath("(I)org/foo/bar/Class;")).isTrue() relocator = SimpleRelocator("^META-INF/org.foo.xml\$", rawString = true) assertThat(relocator.canRelocatePath("META-INF/org.foo.xml")).isTrue() @@ -147,6 +147,32 @@ class SimpleRelocatorTest { .isEqualTo("/org/apache/momentum/mass.properties") } + @Test + fun testCanRelocateAbsClassPathWithExcludes() { + val relocator = SimpleRelocator( + "org/apache/velocity", + "org/apache/momentum", + excludes = listOf("org/apache/velocity/excluded/*"), + ) + assertThat(relocator.canRelocatePath("/org/apache/velocity/mass.properties")).isTrue() + assertThat(relocator.canRelocatePath("org/apache/velocity/mass.properties")).isTrue() + assertThat(relocator.canRelocatePath("/org/apache/velocity/excluded/mass.properties")).isFalse() + assertThat(relocator.canRelocatePath("org/apache/velocity/excluded/mass.properties")).isFalse() + } + + @Test + fun testCanRelocateAbsClassPathWithIncludes() { + val relocator = SimpleRelocator( + "org/apache/velocity", + "org/apache/momentum", + includes = listOf("org/apache/velocity/included/*"), + ) + assertThat(relocator.canRelocatePath("/org/apache/velocity/mass.properties")).isFalse() + assertThat(relocator.canRelocatePath("org/apache/velocity/mass.properties")).isFalse() + assertThat(relocator.canRelocatePath("/org/apache/velocity/included/mass.properties")).isTrue() + assertThat(relocator.canRelocatePath("org/apache/velocity/included/mass.properties")).isTrue() + } + @Test fun testRelocatePath() { var relocator = SimpleRelocator("org.foo") @@ -172,15 +198,138 @@ class SimpleRelocatorTest { @Test fun testRelocateRawString() { var relocator = SimpleRelocator("Lorg/foo", "Lhidden/org/foo", rawString = true) - assertThat(relocator.relocatePath("(I)Lorg/foo/bar/Class")) - .isEqualTo("(I)Lhidden/org/foo/bar/Class") + assertThat(relocator.relocatePath("(I)Lorg/foo/bar/Class;")) + .isEqualTo("(I)Lhidden/org/foo/bar/Class;") relocator = SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", rawString = true) assertThat(relocator.relocatePath("META-INF/org.foo.xml")) .isEqualTo("META-INF/hidden.org.foo.xml") } + @Test + fun testRelocateMavenFiles() { + val relocator = SimpleRelocator( + "META-INF/maven", + "META-INF/shade/maven", + excludes = listOf("META-INF/maven/com.foo.bar/artifactId/pom.*"), + ) + assertThat(relocator.canRelocatePath("META-INF/maven/com.foo.bar/artifactId/pom.properties")).isFalse() + assertThat(relocator.canRelocatePath("META-INF/maven/com.foo.bar/artifactId/pom.xml")).isFalse() + assertThat(relocator.canRelocatePath("META-INF/maven/com/foo/bar/artifactId/pom.properties")).isTrue() + assertThat(relocator.canRelocatePath("META-INF/maven/com/foo/bar/artifactId/pom.xml")).isTrue() + assertThat(relocator.canRelocatePath("META-INF/maven/com-foo-bar/artifactId/pom.properties")).isTrue() + assertThat(relocator.canRelocatePath("META-INF/maven/com-foo-bar/artifactId/pom.xml")).isTrue() + } + + @Test + fun testRelocateSourceWithExcludesRaw() { + val relocator = SimpleRelocator( + "org.apache.maven", + "com.acme.maven", + listOf("foo.bar", "zot.baz"), + listOf("irrelevant.exclude", "org.apache.maven.exclude1", "org.apache.maven.sub.exclude2"), + true, + ) + assertThat(relocator.applyToSourceContent(sourceFile)).isEqualTo(sourceFile) + } + + @Test + fun testRelocateSourceWithExcludes() { + // Main relocator with in-/excludes + val relocator = SimpleRelocator( + "org.apache.maven", + "com.acme.maven", + listOf("foo.bar", "zot.baz"), + listOf("irrelevant.exclude", "org.apache.maven.exclude1", "org.apache.maven.sub.exclude2"), + ) + // Make sure not to replace variables 'io' and 'ioInput', package 'java.io' + val ioRelocator = SimpleRelocator("io", "shaded.io") + // Check corner case which was not working in PR #100 + val asmRelocator = SimpleRelocator("org.objectweb.asm", "aj.org.objectweb.asm") + // Make sure not to replace 'foo' package by path-like 'shaded/foo' + val fooRelocator = SimpleRelocator("foo", "shaded.foo", excludes = listOf("foo.bar")) + assertThat( + fooRelocator.applyToSourceContent( + asmRelocator.applyToSourceContent( + ioRelocator.applyToSourceContent(relocator.applyToSourceContent(sourceFile)), + ), + ), + ).isEqualTo(relocatedFile) + } + private companion object { + val sourceFile = """ + package org.apache.maven.hello; + package org.objectweb.asm; + + import foo.bar.Bar; + import zot.baz.Baz; + import org.apache.maven.exclude1.Ex1; + import org.apache.maven.exclude1.a.b.Ex1AB; + import org.apache.maven.sub.exclude2.Ex2; + import org.apache.maven.sub.exclude2.c.d.Ex2CD; + import org.apache.maven.In; + import org.apache.maven.e.InE; + import org.apache.maven.f.g.InFG; + import java.io.IOException; + + /** + * Also check out {@link org.apache.maven.hello.OtherClass} and {@link + * org.apache.maven.hello.YetAnotherClass} + */ + public class MyClass { + private org.apache.maven.exclude1.x.X myX; + private org.apache.maven.h.H h; + private String ioInput; + + /** Javadoc, followed by default visibility method with fully qualified return type */ + org.apache.maven.MyReturnType doSomething( org.apache.maven.Bar bar, org.objectweb.asm.sub.Something something) { + org.apache.maven.Bar bar; + org.objectweb.asm.sub.Something something; + String io, val; + String noRelocation = "NoWordBoundaryXXXorg.apache.maven.In"; + String relocationPackage = "org.apache.maven.In"; + String relocationPath = "org/apache/maven/In"; + } + } + """.trimIndent() + + val relocatedFile = """ + package com.acme.maven.hello; + package aj.org.objectweb.asm; + + import foo.bar.Bar; + import zot.baz.Baz; + import org.apache.maven.exclude1.Ex1; + import org.apache.maven.exclude1.a.b.Ex1AB; + import org.apache.maven.sub.exclude2.Ex2; + import org.apache.maven.sub.exclude2.c.d.Ex2CD; + import com.acme.maven.In; + import com.acme.maven.e.InE; + import com.acme.maven.f.g.InFG; + import java.io.IOException; + + /** + * Also check out {@link com.acme.maven.hello.OtherClass} and {@link + * com.acme.maven.hello.YetAnotherClass} + */ + public class MyClass { + private org.apache.maven.exclude1.x.X myX; + private com.acme.maven.h.H h; + private String ioInput; + + /** Javadoc, followed by default visibility method with fully qualified return type */ + com.acme.maven.MyReturnType doSomething( com.acme.maven.Bar bar, aj.org.objectweb.asm.sub.Something something) { + com.acme.maven.Bar bar; + aj.org.objectweb.asm.sub.Something something; + String io, val; + String noRelocation = "NoWordBoundaryXXXorg.apache.maven.In"; + String relocationPackage = "com.acme.maven.In"; + String relocationPath = "com/acme/maven/In"; + } + } + """.trimIndent() + fun SimpleRelocator.relocatePath(path: String): String { return relocatePath(RelocatePathContext(path)) } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt index 5f8b31152..8d8d7c016 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt @@ -10,8 +10,8 @@ val testObjectFactory: ObjectFactory = ProjectBuilder.builder().build().objects fun SimpleRelocator( pattern: String? = null, shadedPattern: String? = null, - includes: List? = null, - excludes: List? = null, + includes: List = emptyList(), + excludes: List = emptyList(), rawString: Boolean = false, ): SimpleRelocator = SimpleRelocator( objectFactory = testObjectFactory, From 94664caaaf26f0b0f2d9ee50cdca176fd77373a4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 9 Dec 2024 09:59:08 +0800 Subject: [PATCH 086/941] Update dependency org.ajoberstar.git-publish:gradle-git-publish to v5 (#1095) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build-logic/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 00ca0a991..8b966f0e4 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -11,6 +11,6 @@ dependencies { implementation("com.gradle.publish:plugin-publish-plugin:1.3.0") implementation("com.vanniktech:gradle-maven-publish-plugin:0.30.0") implementation("org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin:1.9.20") - implementation("org.ajoberstar.git-publish:gradle-git-publish:4.2.2") + implementation("org.ajoberstar.git-publish:gradle-git-publish:5.0.0") implementation("com.github.node-gradle:gradle-node-plugin:7.1.0") } From 4aa506e8fecc9ea4cd46bb4df9d83e9c9c43f4db Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 9 Dec 2024 10:03:16 +0800 Subject: [PATCH 087/941] Rename release task to releaseAll --- .github/workflows/release.yml | 2 +- build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e2a1b6a10..f699724b3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: # Due to some limitations of https://github.com/node-gradle/gradle-node-plugin. node-version: '16' # Disable CC due to https://github.com/gradle/gradle/issues/22779 - - run: ./gradlew release --no-configuration-cache + - run: ./gradlew releaseAll --no-configuration-cache env: GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_KEY }} GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_SECRET }} diff --git a/build.gradle.kts b/build.gradle.kts index d0826c9c5..d0d4c6bfc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -140,7 +140,7 @@ tasks.whenTaskAdded { } } -tasks.register("release") { +tasks.register("releaseAll") { description = "Publishes the plugin to maven repos and deploys website." group = PublishingPlugin.PUBLISH_TASK_GROUP From 2c5c137dc29b03330985f56c573af9f19e8e200a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 8 Dec 2024 21:48:37 -0500 Subject: [PATCH 088/941] Simplify SimpleRelocator logic (#1096) * Simplify canRelocatePath * Simplify shadeSourceWithExcludes * Use dropLastWhile for RelativeArchivePath * Revert "Use dropLastWhile for RelativeArchivePath" This reverts commit 832b5ef409b0bf7bd2c78a3ed6f5092fb85be76e. * Filter isNotEmpty --- .../shadow/relocation/SimpleRelocator.kt | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 74d65b0fd..78837b622 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -99,13 +99,9 @@ public open class SimpleRelocator @JvmOverloads constructor( if (rawString) return Pattern.compile(pathPattern).matcher(path).find() // If string is too short - no need to perform expensive string operations if (path.length < pathPattern.length) return false - var adjustedPath = if (path.endsWith(".class")) { - // Safeguard against strings containing only ".class" - if (path.length == 6) return false - path.dropLast(6) - } else { - path - } + var adjustedPath = path.removeSuffix(".class") + // Safeguard against strings containing only ".class" + if (adjustedPath.isEmpty()) return false // Allow for annoying option of an extra / on the front of a path. See MSHADE-119; // comes from getClass().getResource("/a/b/c.properties"). adjustedPath = adjustedPath.removePrefix("/") @@ -200,14 +196,11 @@ public open class SimpleRelocator @JvmOverloads constructor( ): String { // Usually shading makes package names a bit longer, so make buffer 10% bigger than original source val shadedSourceContent = StringBuilder(sourceContent.length * 11 / 10) - var isFirstSnippet = true // Make sure that search pattern starts at word boundary and that we look for literal ".", not regex jokers val snippets = sourceContent.split(("\\b" + patternFrom.replace(".", "[.]") + "\\b").toRegex()) - .dropLastWhile { it.isEmpty() }.toTypedArray() - var i = 0 - val snippetsLength = snippets.size - while (i < snippetsLength) { - val snippet = snippets[i] + .filter(CharSequence::isNotEmpty) + snippets.forEachIndexed { i, snippet -> + val isFirstSnippet = i == 0 val previousSnippet = if (isFirstSnippet) "" else snippets[i - 1] var doExclude = false for (excludedPattern in excludedPatterns) { @@ -218,7 +211,6 @@ public open class SimpleRelocator @JvmOverloads constructor( } if (isFirstSnippet) { shadedSourceContent.append(snippet) - isFirstSnippet = false } else { val previousSnippetOneLine = previousSnippet.replace("\\s+".toRegex(), " ") val afterDotSlashSpace = RX_ENDS_WITH_DOT_SLASH_SPACE.matcher(previousSnippetOneLine).find() @@ -228,7 +220,6 @@ public open class SimpleRelocator @JvmOverloads constructor( .append(if (shouldExclude) patternFrom else patternTo) .append(snippet) } - i++ } return shadedSourceContent.toString() } From e9341b54250591c3bd52ae4a998af6fd96e969a7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 9 Dec 2024 13:31:32 +0800 Subject: [PATCH 089/941] Configure clean tasks --- build.gradle.kts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index d0d4c6bfc..823e242b4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -140,6 +140,15 @@ tasks.whenTaskAdded { } } +tasks.clean { + val includedBuilds = gradle.includedBuilds + dependsOn(includedBuilds.map { it.task(path) }) + + val dirs = includedBuilds.map { it.projectDir } + projectDir + delete.addAll(dirs.map { it.resolve(".gradle") }) + delete.addAll(dirs.map { it.resolve(".kotlin") }) +} + tasks.register("releaseAll") { description = "Publishes the plugin to maven repos and deploys website." group = PublishingPlugin.PUBLISH_TASK_GROUP From 4736c6ae318143fb1e0e990bd3341002898f9ef1 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 9 Dec 2024 14:10:55 +0800 Subject: [PATCH 090/941] Tweak SimpleRelocatorTest style --- .../shadow/relocation/SimpleRelocatorTest.kt | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 0e9861b31..d96d68b6a 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -71,7 +71,10 @@ class SimpleRelocatorTest { @Test fun testCanRelocatePathWithRegex() { // Include with Regex - var relocator = SimpleRelocator("org.foo", includes = listOf("%regex[org/foo/R(\\\$.*)?\$]")) + var relocator = SimpleRelocator( + "org.foo", + includes = listOf("%regex[org/foo/R(\\\$.*)?\$]"), + ) assertThat(relocator.canRelocatePath("org/foo/R.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/R\$string.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/R\$layout.class")).isTrue() @@ -82,8 +85,10 @@ class SimpleRelocatorTest { assertThat(relocator.canRelocatePath("org/R\$string.class")).isFalse() // Exclude with Regex - relocator = SimpleRelocator("org.foo") - relocator.exclude("%regex[org/foo/.*Factory[0-9].*]") + relocator = SimpleRelocator( + "org.foo", + excludes = listOf("%regex[org/foo/.*Factory[0-9].*]"), + ) assertThat(relocator.canRelocatePath("org/foo/Factory.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/FooFactoryMain.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/BarFactory.class")).isTrue() @@ -247,7 +252,11 @@ class SimpleRelocatorTest { // Check corner case which was not working in PR #100 val asmRelocator = SimpleRelocator("org.objectweb.asm", "aj.org.objectweb.asm") // Make sure not to replace 'foo' package by path-like 'shaded/foo' - val fooRelocator = SimpleRelocator("foo", "shaded.foo", excludes = listOf("foo.bar")) + val fooRelocator = SimpleRelocator( + "foo", + "shaded.foo", + excludes = listOf("foo.bar"), + ) assertThat( fooRelocator.applyToSourceContent( asmRelocator.applyToSourceContent( From b016d84669ee50d05b71ef22c3cc568b6d1a5b2c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 9 Dec 2024 03:22:42 -0500 Subject: [PATCH 091/941] Add test cases for `SimpleRelocator` (#1094) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Lucas Rogerio Caetano Ferreira Co-authored-by: zinking Co-authored-by: Hasan Demirtaş --- .../shadow/relocation/SimpleRelocatorTest.kt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index d96d68b6a..37be193dd 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -226,6 +226,52 @@ class SimpleRelocatorTest { assertThat(relocator.canRelocatePath("META-INF/maven/com-foo-bar/artifactId/pom.xml")).isTrue() } + @Test + fun testCanRelocateExcludedSourceFile() { + val relocator = SimpleRelocator( + "org.foo", + excludes = listOf("org/apache/iceberg/spark/parquet/**", "org/apache/spark/sql/execution/datasources/parquet/**"), + ) + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isFalse() + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet\$.class")).isFalse() + assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() + } + + @Test + fun testCanRelocateExcludedSourceFileWithRegex() { + val relocator = SimpleRelocator( + "org.foo", + excludes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]"), + ) + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isFalse() + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet\$.class")).isFalse() + assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isFalse() + assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() + } + + @Test + fun testCanRelocateIncludedSourceFile() { + val relocator = SimpleRelocator( + includes = listOf("org/apache/iceberg/spark/parquet/**", "org/apache/spark/sql/execution/datasources/parquet/**"), + ) + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isTrue() + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet\$.class")).isTrue() + assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/Class.class")).isFalse() + } + + @Test + fun testCanRelocateIncludedSourceFileWithRegex() { + val relocator = SimpleRelocator( + includes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]"), + ) + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isTrue() + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet\$.class")).isTrue() + assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isTrue() + assertThat(relocator.canRelocatePath("org/foo/Class.class")).isFalse() + } + @Test fun testRelocateSourceWithExcludesRaw() { val relocator = SimpleRelocator( From 433329faad525eeabab185e241def5b3110a223f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 9 Dec 2024 08:54:16 -0500 Subject: [PATCH 092/941] Clean up maven repo test kits (#1097) --- .../util/AppendableMavenFileModule.groovy | 13 +- .../util/AppendableMavenFileRepository.groovy | 3 +- .../shadow/util/repo/AbstractModule.groovy | 8 - .../repo/maven/AbstractMavenModule.groovy | 166 +++--------------- .../repo/maven/DefaultMavenMetaData.groovy | 39 ---- .../util/repo/maven/MavenDependency.groovy | 19 -- .../util/repo/maven/MavenFileModule.groovy | 40 ++--- .../repo/maven/MavenFileRepository.groovy | 3 +- .../util/repo/maven/MavenMetaData.groovy | 6 - .../shadow/util/repo/maven/MavenModule.groovy | 24 --- .../shadow/util/repo/maven/MavenPom.groovy | 44 ----- .../util/repo/maven/MavenRepository.groovy | 2 +- .../shadow/util/repo/maven/MavenScope.groovy | 29 --- 13 files changed, 38 insertions(+), 358 deletions(-) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenDependency.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenMetaData.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenScope.groovy diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy index 689f84994..2e9022905 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy @@ -7,7 +7,7 @@ import org.apache.commons.io.IOUtils @InheritConstructors class AppendableMavenFileModule extends MavenFileModule { - Map> contents = [:].withDefault { [:] } + Map> contents = [:].withDefault { [:] } as Map> Map files = [:] AppendableMavenFileModule use(File file) { @@ -57,15 +57,4 @@ class AppendableMavenFileModule extends MavenFileModule { builder.build() } } - - /** - * Adds an additional artifact to this module. - * @param options Can specify any of: type or classifier - */ - @Override - AppendableMavenFileModule artifact(Map options) { - artifacts << options - return this - } - } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy index cc79dc0b6..18e4331da 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy @@ -7,9 +7,8 @@ import groovy.transform.InheritConstructors class AppendableMavenFileRepository extends MavenFileRepository { @Override - AppendableMavenFileModule module(String groupId, String artifactId, Object version = '1.0') { + AppendableMavenFileModule module(String groupId, String artifactId, String version = '1.0') { def artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") return new AppendableMavenFileModule(artifactDir, groupId, artifactId, version as String) } - } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy index 7e5b983d0..8b99bf018 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy @@ -46,18 +46,10 @@ abstract class AbstractModule { protected abstract onPublish(File file) - static File getSha1File(File file) { - getHashFile(file, "sha1") - } - static File sha1File(File file) { hashFile(file, "sha1", 40) } - static File getMd5File(File file) { - getHashFile(file, "md5") - } - static File md5File(File file) { hashFile(file, "md5", 32) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy index e08a29ad9..aa3ec713b 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy @@ -28,27 +28,6 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule this.version = version } - @Override - MavenModule parent(String group, String artifactId, String version) { - parentPomSection = """ - - ${group} - ${artifactId} - ${version} - -""" - return this - } - - @Override - File getArtifactFile(Map options = [:]) { - if (version.endsWith("-SNAPSHOT") && !metaDataFile.exists() && uniqueSnapshots) { - def artifact = toArtifact(options) - return moduleDir.resolve("${artifactId}-${version}${artifact.classifier ? "-${artifact.classifier}" : ""}.${artifact.type}") - } - return artifactFile(options) - } - abstract boolean getUniqueSnapshots() String getPublishArtifactVersion() { @@ -61,7 +40,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule private String getUniqueSnapshotVersion() { assert uniqueSnapshots && version.endsWith('-SNAPSHOT') if (metaDataFile.isFile()) { - def metaData = new XmlParser().parse(metaDataFile.assertIsFile()) + def metaData = new XmlParser().parse(metaDataFile) def timestamp = metaData.versioning.snapshot.timestamp[0].text().trim() def build = metaData.versioning.snapshot.buildNumber[0].text().trim() return "${timestamp}-${build}" @@ -77,35 +56,11 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule } @Override - MavenModule dependsOn(String group, String artifactId, String version, String type = null) { + MavenModule dependsOn(String group, String artifactId, String version) { this.dependencies << [groupId: group, artifactId: artifactId, version: version, type: type] return this } - @Override - MavenModule hasPackaging(String packaging) { - this.packaging = packaging - return this - } - - /** - * Specifies the type of the main artifact. - */ - @Override - MavenModule hasType(String type) { - this.type = type - return this - } - - /** - * Adds an additional artifact to this module. - * @param options Can specify any of: type or classifier - */ - MavenModule artifact(Map options) { - artifacts << options - return this - } - String getPackaging() { return packaging } @@ -114,78 +69,6 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule return dependencies } - List getArtifacts() { - return artifacts - } - - void assertNotPublished() { - pomFile.assertDoesNotExist() - } - - void assertPublished() { - assert pomFile.assertExists() - assert parsedPom.groupId == groupId - assert parsedPom.artifactId == artifactId - assert parsedPom.version == version - } - - void assertPublishedAsPomModule() { - assertPublished() - assertArtifactsPublished("${artifactId}-${publishArtifactVersion}.pom") - assert parsedPom.packaging == "pom" - } - - void assertPublishedAsJavaModule() { - assertPublished() - assertArtifactsPublished("${artifactId}-${publishArtifactVersion}.jar", "${artifactId}-${publishArtifactVersion}.pom") - assert parsedPom.packaging == null - } - - void assertPublishedAsWebModule() { - assertPublished() - assertArtifactsPublished("${artifactId}-${publishArtifactVersion}.war", "${artifactId}-${publishArtifactVersion}.pom") - assert parsedPom.packaging == 'war' - } - - void assertPublishedAsEarModule() { - assertPublished() - assertArtifactsPublished("${artifactId}-${publishArtifactVersion}.ear", "${artifactId}-${publishArtifactVersion}.pom") - assert parsedPom.packaging == 'ear' - } - - /** - * Asserts that exactly the given artifacts have been deployed, along with their checksum files - */ - void assertArtifactsPublished(String... names) { - def artifactNames = names as Set - if (publishesMetaDataFile()) { - artifactNames.add(MAVEN_METADATA_FILE) - } - assert moduleDir.isDirectory() - Set actual = moduleDir.list() as Set - for (name in artifactNames) { - assert actual.remove(name) - - if (publishesHashFiles()) { - assert actual.remove("${name}.md5" as String) - assert actual.remove("${name}.sha1" as String) - } - } - assert actual.isEmpty() - } - - //abstract String getPublishArtifactVersion() - - @Override - MavenPom getParsedPom() { - return new MavenPom(pomFile) - } - - @Override - DefaultMavenMetaData getRootMetaData() { - new DefaultMavenMetaData(rootMetaDataFile) - } - @Override File getPomFile() { return moduleDir.resolve("$artifactId-${publishArtifactVersion}.pom") @@ -209,12 +92,6 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule return moduleDir.resolve(fileName) } - @Override - MavenModule publishWithChangedContent() { - publishCount++ - return publish() - } - protected Map toArtifact(Map options) { options = new HashMap(options) def artifact = [type: options.remove('type') ?: type, classifier: options.remove('classifier') ?: null] @@ -242,38 +119,37 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule publish(pomFile) { Writer writer -> def pomPackaging = packaging ?: type writer << """ - - - 4.0.0 - $groupId - $artifactId - $pomPackaging - $version - Published on $publishTimestamp""" + + + 4.0.0 + $groupId + $artifactId + $pomPackaging + $version + Published on $publishTimestamp + """.stripIndent() if (parentPomSection) { writer << "\n$parentPomSection\n" } if (!dependencies.empty) { - writer << """ - """ + writer << "" } dependencies.each { dependency -> def typeAttribute = dependency['type'] == null ? "" : "$dependency.type" writer << """ - - $dependency.groupId - $dependency.artifactId - $dependency.version - $typeAttribute - """ + + $dependency.groupId + $dependency.artifactId + $dependency.version + $typeAttribute + """.stripIndent() } if (!dependencies.empty) { - writer << """ - """ + writer << "" } writer << "\n" @@ -316,7 +192,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule publishPom() artifacts.each { artifact -> - publishArtifact(artifact) + publishArtifact(artifact as Map) } publishArtifact([:]) return this @@ -339,6 +215,4 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule } protected abstract boolean publishesMetaDataFile() - - protected abstract boolean publishesHashFiles() } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy deleted file mode 100644 index bccdb40f1..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/DefaultMavenMetaData.groovy +++ /dev/null @@ -1,39 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import groovy.xml.XmlParser - -/** - * http://maven.apache.org/ref/3.0.1/maven-repository-metadata/repository-metadata.html - */ -class DefaultMavenMetaData implements MavenMetaData { - - String text - - String groupId - String artifactId - String version - - String lastUpdated - - DefaultMavenMetaData(File file) { - text = file.text - def xml = new XmlParser().parseText(text) - - groupId = xml.groupId[0]?.text() - artifactId = xml.artifactId[0]?.text() - version = xml.version[0]?.text() - - def versioning = xml.versioning[0] - - lastUpdated = versioning.lastUpdated[0]?.text() - - versioning.versions[0].version.each { - versions << it.text() - } - } - - @Override - List getVersions() { - return [] - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenDependency.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenDependency.groovy deleted file mode 100644 index 56db25320..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenDependency.groovy +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -class MavenDependency { - String groupId - String artifactId - String version - String classifier - String type - - MavenDependency hasType(def type) { - assert this.type == type - return this - } - - @Override - String toString() { - return String.format("MavenDependency %s:%s:%s:%s@%s", groupId, artifactId, version, classifier, type) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy index 71233befb..fad253beb 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - class MavenFileModule extends AbstractMavenModule { private boolean uniqueSnapshots = true @@ -13,29 +12,23 @@ class MavenFileModule extends AbstractMavenModule { return uniqueSnapshots } - @Override - MavenModule withNonUniqueSnapshots() { - uniqueSnapshots = false - return this - } - @Override String getMetaDataFileContent() { """ - - - $groupId - $artifactId - $version - - - ${timestampFormat.format(publishTimestamp)} - $publishCount - - ${updateFormat.format(publishTimestamp)} - - -""" + + + $groupId + $artifactId + $version + + + ${timestampFormat.format(publishTimestamp)} + $publishCount + + ${updateFormat.format(publishTimestamp)} + + + """.stripIndent() } @Override @@ -48,9 +41,4 @@ class MavenFileModule extends AbstractMavenModule { protected boolean publishesMetaDataFile() { uniqueSnapshots && version.endsWith("-SNAPSHOT") } - - @Override - protected boolean publishesHashFiles() { - true - } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy index 455588315..fdff3a8ed 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - /** * A fixture for dealing with file Maven repositories. */ @@ -17,7 +16,7 @@ class MavenFileRepository implements MavenRepository { } @Override - MavenFileModule module(String groupId, String artifactId, Object version = '1.0') { + MavenFileModule module(String groupId, String artifactId, String version = '1.0') { def artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") return new MavenFileModule(artifactDir, groupId, artifactId, version as String) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenMetaData.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenMetaData.groovy deleted file mode 100644 index 0a3b49b11..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenMetaData.groovy +++ /dev/null @@ -1,6 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -interface MavenMetaData { - List getVersions(); - -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy index fcd466d10..dd74383e3 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - interface MavenModule { /** * Publishes the pom.xml plus main artifact, plus any additional artifacts for this module. Publishes only those artifacts whose content has @@ -13,32 +12,9 @@ interface MavenModule { */ MavenModule publishPom() - /** - * Publishes the pom.xml plus main artifact, plus any additional artifacts for this module, with different content (and size) to any - * previous publication. - */ - MavenModule publishWithChangedContent() - - MavenModule withNonUniqueSnapshots() - - MavenModule parent(String group, String artifactId, String version) - MavenModule dependsOn(String group, String artifactId, String version) - MavenModule hasPackaging(String packaging) - - /** - * Sets the type of the main artifact for this module. - */ - MavenModule hasType(String type) - File getPomFile() - File getArtifactFile() - File getMetaDataFile() - - MavenPom getParsedPom() - - MavenMetaData getRootMetaData() } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy deleted file mode 100644 index 9aa3d1ba4..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenPom.groovy +++ /dev/null @@ -1,44 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import groovy.xml.XmlParser - -class MavenPom { - String groupId - String artifactId - String version - String packaging - String description - final Map scopes = [:] - - MavenPom(File pomFile) { - if (pomFile.exists()) { - def pom = new XmlParser().parse(pomFile) - - groupId = pom.groupId[0]?.text() - artifactId = pom.artifactId[0]?.text() - version = pom.version[0]?.text() - packaging = pom.packaging[0]?.text() - description = pom.description[0]?.text() - - pom.dependencies.dependency.each { dep -> - def scopeElement = dep.scope - def scopeName = scopeElement ? scopeElement.text() : "runtime" - def scope = scopes[scopeName] - if (!scope) { - scope = new MavenScope() - scopes[scopeName] = scope - } - MavenDependency mavenDependency = new MavenDependency( - groupId: dep.groupId.text(), - artifactId: dep.artifactId.text(), - version: dep.version.text(), - classifier: dep.classifier ? dep.classifier.text() : null, - type: dep.type ? dep.type.text() : null - ) - def key = "${mavenDependency.groupId}:${mavenDependency.artifactId}:${mavenDependency.version}" - key += mavenDependency.classifier ? ":${mavenDependency.classifier}" : "" - scope.dependencies[key] = mavenDependency - } - } - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy index 27b077cec..614b083bb 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy @@ -8,5 +8,5 @@ interface MavenRepository { MavenModule module(String groupId, String artifactId) - MavenModule module(String groupId, String artifactId, Object version) + MavenModule module(String groupId, String artifactId, String version) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenScope.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenScope.groovy deleted file mode 100644 index 8b456d148..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenScope.groovy +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import org.apache.commons.lang3.StringUtils - -class MavenScope { - Map dependencies = [:] - - void assertDependsOn(String[] expected) { - assert dependencies.size() == expected.length - expected.each { - String key = StringUtils.substringBefore(it, "@") - def dependency = expectDependency(key) - - String type = null - if (it != key) { - type = StringUtils.substringAfter(it, "@") - } - assert dependency.hasType(type) - } - } - - MavenDependency expectDependency(String key) { - final dependency = dependencies[key] - if (dependency == null) { - throw new AssertionError("Could not find expected dependency $key. Actual: ${dependencies.values()}") - } - return dependency - } -} From 9db55a70ebf30b19ddf7b39575f65931755525f2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 10 Dec 2024 15:43:59 +0800 Subject: [PATCH 093/941] Update dependency org.ajoberstar.git-publish:gradle-git-publish to v5.1.0 (#1099) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- .github/workflows/release.yml | 11 +++++++---- build-logic/build.gradle.kts | 2 +- .../kotlin/shadow.convention.deploy.gradle.kts | 18 ++++++------------ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f699724b3..a3e9d3ca1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,15 +24,18 @@ jobs: with: # Due to some limitations of https://github.com/node-gradle/gradle-node-plugin. node-version: '16' + # Used for gitPublish tasks. + - name: Set up Git + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' # Disable CC due to https://github.com/gradle/gradle/issues/22779 - run: ./gradlew releaseAll --no-configuration-cache env: GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_KEY }} GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_SECRET }} - # We must declare repository_owner as the user, workaround for https://github.com/ajoberstar/gradle-git-publish/issues/109. - GRGIT_USER: ${{ github.repository_owner }} - # https://ajoberstar.org/grgit/main/grgit-authentication.html#_environment_variables - GRGIT_PASS: ${{ secrets.GITHUB_TOKEN }} + GITHUB_USER: ${{ github.repository_owner }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 8b966f0e4..997d037f7 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -11,6 +11,6 @@ dependencies { implementation("com.gradle.publish:plugin-publish-plugin:1.3.0") implementation("com.vanniktech:gradle-maven-publish-plugin:0.30.0") implementation("org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin:1.9.20") - implementation("org.ajoberstar.git-publish:gradle-git-publish:5.0.0") + implementation("org.ajoberstar.git-publish:gradle-git-publish:5.1.0") implementation("com.github.node-gradle:gradle-node-plugin:7.1.0") } diff --git a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts index 39f4dd10f..72432ea02 100644 --- a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts @@ -8,10 +8,12 @@ plugins { gitPublish { repoUri = "https://github.com/GradleUp/shadow.git" branch = "gh-pages" + username = providers.environmentVariable("GITHUB_USER") + password = providers.environmentVariable("GITHUB_TOKEN") contents { - from("build/site") - into("api") { - from(tasks.named("dokkaHtml")) + from(yarnBuild) + from(tasks.named("dokkaHtml")) { + into("api") } filter( "tokens" to mapOf( @@ -22,16 +24,8 @@ gitPublish { } } -node { - yarnVersion = "1.5.1" -} - val yarnBuild = tasks.named("yarn_build") { + dependsOn(tasks.yarn) inputs.files(fileTree("src/docs")) outputs.dir(file("build/site")) - dependsOn(tasks.yarn) -} - -tasks.gitPublishCopy { - dependsOn(yarnBuild, tasks.named("dokkaHtml")) } From de443efa711df9c86b6b0595963a31b7b29dac89 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 10 Dec 2024 15:48:27 +0800 Subject: [PATCH 094/941] Fix gitPublish --- .../main/kotlin/shadow.convention.deploy.gradle.kts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts index 72432ea02..b9ea53708 100644 --- a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts @@ -5,6 +5,12 @@ plugins { id("com.github.node-gradle.node") } +val yarnBuild = tasks.named("yarn_build") { + dependsOn(tasks.yarn) + inputs.files(fileTree("src/docs")) + outputs.dir(file("build/site")) +} + gitPublish { repoUri = "https://github.com/GradleUp/shadow.git" branch = "gh-pages" @@ -23,9 +29,3 @@ gitPublish { ) } } - -val yarnBuild = tasks.named("yarn_build") { - dependsOn(tasks.yarn) - inputs.files(fileTree("src/docs")) - outputs.dir(file("build/site")) -} From dd5efe71d363b8cd99c7a4a596f9b749575dc53a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 10 Dec 2024 15:54:12 +0800 Subject: [PATCH 095/941] Delete node_modules when clean task running --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index 823e242b4..8cdb3d17e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -147,6 +147,7 @@ tasks.clean { val dirs = includedBuilds.map { it.projectDir } + projectDir delete.addAll(dirs.map { it.resolve(".gradle") }) delete.addAll(dirs.map { it.resolve(".kotlin") }) + delete.add("node_modules") } tasks.register("releaseAll") { From b7f4d142234c606d19c25a4b7ac6e7879df846a0 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 10 Dec 2024 03:19:09 -0500 Subject: [PATCH 096/941] Add a logo for IDEA to display --- .idea/icon.svg | 3835 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 3835 insertions(+) create mode 100644 .idea/icon.svg diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 000000000..4dbc6611b --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,3835 @@ + + + + From e5ff03cf2e0f621ca51d6cabc8fb3cc43a0b2a7c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 11 Dec 2024 00:05:25 -0500 Subject: [PATCH 097/941] Convert some functional test kits to Kotlin (#1100) * Mark funcTest depend on the output of intiTest * Convert AppendableJar and JarBuilder * Convert HashUtil and HashValue * Convert AbstractModule * Convert MavenModule * Convert MavenRepository --- build.gradle.kts | 1 + .../plugins/shadow/util/AppendableJar.groovy | 25 ----- .../gradle/plugins/shadow/util/HashUtil.java | 93 ------------------- .../gradle/plugins/shadow/util/HashValue.java | 75 --------------- .../plugins/shadow/util/JarBuilder.groovy | 51 ---------- .../shadow/util/repo/AbstractModule.groovy | 71 -------------- .../repo/maven/AbstractMavenModule.groovy | 12 +-- .../util/repo/maven/MavenFileModule.groovy | 4 +- .../shadow/util/repo/maven/MavenModule.groovy | 20 ---- .../util/repo/maven/MavenRepository.groovy | 12 --- .../plugins/shadow/util/AppendableJar.kt | 20 ++++ .../gradle/plugins/shadow/util/HashUtil.kt | 54 +++++++++++ .../gradle/plugins/shadow/util/HashValue.kt | 7 ++ .../gradle/plugins/shadow/util/JarBuilder.kt | 42 +++++++++ .../shadow/util/repo/AbstractModule.kt | 63 +++++++++++++ .../shadow/util/repo/maven/MavenModule.kt | 22 +++++ .../shadow/util/repo/maven/MavenRepository.kt | 14 +++ 17 files changed, 232 insertions(+), 354 deletions(-) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt diff --git a/build.gradle.kts b/build.gradle.kts index 8cdb3d17e..42fbbde71 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -86,6 +86,7 @@ dependencies { exclude(group = "org.hamcrest") } funcTestImplementation(sourceSets.main.get().output) + funcTestImplementation(intiTest.output) lintChecks(libs.androidx.gradlePluginLints) lintChecks(libs.assertk.lint) diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy deleted file mode 100644 index b6fad7012..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.groovy +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -class AppendableJar { - - Map contents = [:] - File file - - AppendableJar(File file) { - this.file = file - } - - AppendableJar insertFile(String path, String content) { - contents[path] = content - return this - } - - File write() { - JarBuilder builder = new JarBuilder(file.newOutputStream()) - contents.each { path, contents -> - builder.withFile(path, contents) - } - builder.build() - return file - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java deleted file mode 100644 index b4cb8404d..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util; - -import org.gradle.api.UncheckedIOException; -import org.gradle.internal.UncheckedException; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class HashUtil { - public static HashValue createHash(File file, String algorithm) { - try { - return createHash(new FileInputStream(file), algorithm); - } catch (UncheckedIOException e) { - // Catch any unchecked io exceptions and add the file path for troubleshooting - throw new UncheckedIOException(String.format("Failed to create %s hash for file %s.", algorithm, file.getAbsolutePath()), e.getCause()); - } catch (FileNotFoundException e) { - throw new UncheckedIOException(e); - } - } - - private static HashValue createHash(InputStream instr, String algorithm) { - MessageDigest messageDigest; - try { - messageDigest = createMessageDigest(algorithm); - byte[] buffer = new byte[4096]; - try { - while (true) { - int nread = instr.read(buffer); - if (nread < 0) { - break; - } - messageDigest.update(buffer, 0, nread); - } - } finally { - instr.close(); - } - } catch (IOException e) { - throw new UncheckedIOException(e); - } - return new HashValue(messageDigest.digest()); - } - - private static MessageDigest createMessageDigest(String algorithm) { - try { - return MessageDigest.getInstance(algorithm); - } catch (NoSuchAlgorithmException e) { - throw UncheckedException.throwAsUncheckedException(e); - } - } - - public static HashValue sha1(byte[] bytes) { - return createHash(new ByteArrayInputStream(bytes), "SHA1"); - } - - public static HashValue sha1(InputStream inputStream) { - return createHash(inputStream, "SHA1"); - } - - public static HashValue md5(File file) { - return createHash(file, "MD5"); - } - - public static HashValue sha1(File file) { - return createHash(file, "SHA1"); - } - - public static HashValue sha256(byte[] bytes) { - return createHash(new ByteArrayInputStream(bytes), "SHA-256"); - } - - public static HashValue sha256(InputStream inputStream) { - return createHash(inputStream, "SHA-256"); - } - - public static HashValue sha256(File file) { - return createHash(file, "SHA-256"); - } - - public static HashValue sha512(InputStream inputStream) { - return createHash(inputStream, "SHA-512"); - } - - public static HashValue sha512(File file) { - return createHash(file, "SHA-512"); - } -} - diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java deleted file mode 100644 index 967afe7ea..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/HashValue.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util; - -import java.math.BigInteger; - -public class HashValue { - private final BigInteger digest; - - public HashValue(byte[] digest) { - this.digest = new BigInteger(1, digest); - } - - public HashValue(String hexString) { - this.digest = new BigInteger(hexString, 16); - } - - public static HashValue parse(String inputString) { - if (inputString == null || inputString.isEmpty()) { - return null; - } - return new HashValue(parseInput(inputString)); - } - - private static String parseInput(String inputString) { - if (inputString == null) { - return null; - } - String cleaned = inputString.trim().toLowerCase(); - int spaceIndex = cleaned.indexOf(' '); - if (spaceIndex != -1) { - String firstPart = cleaned.substring(0, spaceIndex); - if (firstPart.startsWith("md") || firstPart.startsWith("sha")) { - cleaned = cleaned.substring(cleaned.lastIndexOf(' ') + 1); - } else if (firstPart.endsWith(":")) { - cleaned = cleaned.substring(spaceIndex + 1).replace(" ", ""); - } else { - cleaned = cleaned.substring(0, spaceIndex); - } - } - return cleaned; - } - - public String asCompactString() { - return digest.toString(36); - } - - public String asHexString() { - return digest.toString(16); - } - - public byte[] asByteArray() { - return digest.toByteArray(); - } - - public BigInteger asBigInteger() { - return digest; - } - - @Override - public boolean equals(Object other) { - if (this == other) { - return true; - } - if (!(other instanceof HashValue)) { - return false; - } - - HashValue otherHashValue = (HashValue) other; - return digest.equals(otherHashValue.digest); - } - - @Override - public int hashCode() { - return digest.hashCode(); - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy deleted file mode 100644 index b24eb994c..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.groovy +++ /dev/null @@ -1,51 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import org.codehaus.plexus.util.IOUtil - -import java.util.jar.JarEntry -import java.util.jar.JarOutputStream - -class JarBuilder { - - List entries = [] - JarOutputStream jos - - JarBuilder(OutputStream os) { - jos = new JarOutputStream(os) - } - - private void addDirectory(String name) { - if (!entries.contains(name)) { - if (name.lastIndexOf('/') > 0) { - String parent = name.substring(0, name.lastIndexOf('/')) - if (!entries.contains(parent)) { - addDirectory(parent) - } - } - - // directory entries must end in "/" - JarEntry entry = new JarEntry(name + "/") - jos.putNextEntry(entry) - - entries.add(name) - } - } - - JarBuilder withFile(String path, String data) { - def idx = path.lastIndexOf('/') - if (idx != -1) { - addDirectory(path.substring(0, idx)) - } - if (!entries.contains(path)) { - JarEntry entry = new JarEntry(path) - jos.putNextEntry(entry) - entries << path - IOUtil.copy(data.bytes, jos) - } - return this - } - - void build() { - jos.close() - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy deleted file mode 100644 index 8b99bf018..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.groovy +++ /dev/null @@ -1,71 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo - -import com.github.jengelman.gradle.plugins.shadow.util.HashUtil - -abstract class AbstractModule { - /** - * @param cl A closure that is passed a writer to use to generate the content. - */ - protected void publish(File file, Closure cl) { - def hashBefore = file.exists() ? getHash(file, "sha1") : null - def tmpFile = file.parentFile.resolve("${file.name}.tmp") - - tmpFile.withWriter("utf-8") { - cl.call(it) - } - - def hashAfter = getHash(tmpFile, "sha1") - if (hashAfter == hashBefore) { - // Already published - return - } - - assert !file.exists() || file.delete() - assert tmpFile.renameTo(file) - onPublish(file) - } - - protected void publishWithStream(File file, Closure cl) { - def hashBefore = file.exists() ? getHash(file, "sha1") : null - def tmpFile = file.parentFile.resolve("${file.name}.tmp") - - tmpFile.withOutputStream { - cl.call(it) - } - - def hashAfter = getHash(tmpFile, "sha1") - if (hashAfter == hashBefore) { - // Already published - return - } - - assert !file.exists() || file.delete() - assert tmpFile.renameTo(file) - onPublish(file) - } - - protected abstract onPublish(File file) - - static File sha1File(File file) { - hashFile(file, "sha1", 40) - } - - static File md5File(File file) { - hashFile(file, "md5", 32) - } - - private static File hashFile(File file, String algorithm, int len) { - def hashFile = getHashFile(file, algorithm) - def hash = getHash(file, algorithm) - hashFile.text = String.format("%0${len}x", hash) - return hashFile - } - - private static File getHashFile(File file, String algorithm) { - file.parentFile.resolve("${file.name}.${algorithm}") - } - - private static BigInteger getHash(File file, String algorithm) { - HashUtil.createHash(file, algorithm.toUpperCase()).asBigInteger() - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy index aa3ec713b..90dddd2f6 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy @@ -56,8 +56,8 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule } @Override - MavenModule dependsOn(String group, String artifactId, String version) { - this.dependencies << [groupId: group, artifactId: artifactId, version: version, type: type] + MavenModule dependsOn(String groupId, String artifactId, String version) { + this.dependencies << [groupId: groupId, artifactId: artifactId, version: version, type: type] return this } @@ -111,12 +111,12 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule updateRootMavenMetaData(rootMavenMetaData) if (publishesMetaDataFile()) { - publish(metaDataFile) { Writer writer -> + publishWithWriter(metaDataFile) { Writer writer -> writer << getMetaDataFileContent() } } - publish(pomFile) { Writer writer -> + publishWithWriter(pomFile) { Writer writer -> def pomPackaging = packaging ?: type writer << """ @@ -160,7 +160,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule private void updateRootMavenMetaData(File rootMavenMetaData) { def allVersions = rootMavenMetaData.exists() ? new XmlParser().parseText(rootMavenMetaData.text).versioning.versions.version*.value().flatten() : [] allVersions << version - publish(rootMavenMetaData) { Writer writer -> + publishWithWriter(rootMavenMetaData) { Writer writer -> def builder = new MarkupBuilder(writer) builder.metadata { groupId(groupId) @@ -203,7 +203,7 @@ abstract class AbstractMavenModule extends AbstractModule implements MavenModule if (type == 'pom') { return artifactFile } - publish(artifactFile) { Writer writer -> + publishWithWriter(artifactFile) { Writer writer -> writer << "${artifactFile.name} : $artifactContent" } return artifactFile diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy index fad253beb..80d482224 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy @@ -1,5 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven +import org.jetbrains.annotations.NotNull + class MavenFileModule extends AbstractMavenModule { private boolean uniqueSnapshots = true @@ -32,7 +34,7 @@ class MavenFileModule extends AbstractMavenModule { } @Override - protected onPublish(File file) { + protected void onPublish(@NotNull File file) { sha1File(file) md5File(file) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy deleted file mode 100644 index dd74383e3..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.groovy +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -interface MavenModule { - /** - * Publishes the pom.xml plus main artifact, plus any additional artifacts for this module. Publishes only those artifacts whose content has - * changed since the last call to {@code #publish()}. - */ - MavenModule publish() - - /** - * Publishes the pom.xml only - */ - MavenModule publishPom() - - MavenModule dependsOn(String group, String artifactId, String version) - - File getPomFile() - - File getMetaDataFile() -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy deleted file mode 100644 index 614b083bb..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.groovy +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -/** - * A fixture for dealing with Maven repositories. - */ -interface MavenRepository { - URI getUri() - - MavenModule module(String groupId, String artifactId) - - MavenModule module(String groupId, String artifactId, String version) -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt new file mode 100644 index 000000000..798a06bf7 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt @@ -0,0 +1,20 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import java.io.File + +class AppendableJar(private val file: File) { + private val contents = mutableMapOf() + + fun insertFile(path: String, content: String): AppendableJar = apply { + contents[path] = content + } + + fun write(): File { + val builder = JarBuilder(file.outputStream()) + contents.forEach { (path, content) -> + builder.withFile(path, content) + } + builder.build() + return file + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt new file mode 100644 index 000000000..492a8a526 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt @@ -0,0 +1,54 @@ +package com.github.jengelman.gradle.plugins.shadow.util +import java.io.File +import java.io.FileInputStream +import java.io.FileNotFoundException +import java.io.IOException +import java.io.InputStream +import java.io.UncheckedIOException +import java.security.MessageDigest +import java.security.NoSuchAlgorithmException +import org.gradle.internal.UncheckedException + +object HashUtil { + fun createHash(file: File, algorithm: String): HashValue { + try { + return createHash(FileInputStream(file), algorithm) + } catch (e: UncheckedIOException) { + // Catch any unchecked io exceptions and add the file path for troubleshooting + throw UncheckedIOException( + "Failed to create $algorithm hash for file ${file.absolutePath}.", + e.cause, + ) + } catch (e: FileNotFoundException) { + throw UncheckedIOException(e) + } + } + + private fun createHash(inputStream: InputStream, algorithm: String): HashValue { + val messageDigest: MessageDigest + try { + messageDigest = createMessageDigest(algorithm) + val buffer = ByteArray(4096) + inputStream.use { + while (true) { + val nread = it.read(buffer) + if (nread < 0) { + break + } + messageDigest.update(buffer, 0, nread) + } + } + } catch (e: IOException) { + throw UncheckedIOException(e) + } + return HashValue(messageDigest.digest()) + } + + private fun createMessageDigest(algorithm: String): MessageDigest { + try { + return MessageDigest.getInstance(algorithm) + } catch (e: NoSuchAlgorithmException) { + throw UncheckedException.throwAsUncheckedException(e) + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt new file mode 100644 index 000000000..821a0953b --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt @@ -0,0 +1,7 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import java.math.BigInteger + +data class HashValue(val digest: BigInteger) { + constructor(digest: ByteArray) : this(BigInteger(1, digest)) +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt new file mode 100644 index 000000000..d4c81cabb --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt @@ -0,0 +1,42 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import java.io.OutputStream +import java.util.jar.JarEntry +import java.util.jar.JarOutputStream + +class JarBuilder(os: OutputStream) { + private val entries = mutableListOf() + private val jos = JarOutputStream(os) + + private fun addDirectory(name: String) { + if (!entries.contains(name)) { + val parent = name.substringBeforeLast('/', "") + if (parent.isNotEmpty() && !entries.contains(parent)) { + addDirectory(parent) + } + + // directory entries must end in "/" + val entry = JarEntry("$name/") + jos.putNextEntry(entry) + entries.add(name) + } + } + + fun withFile(path: String, data: String): JarBuilder { + val idx = path.lastIndexOf('/') + if (idx != -1) { + addDirectory(path.substring(0, idx)) + } + if (!entries.contains(path)) { + val entry = JarEntry(path) + jos.putNextEntry(entry) + entries.add(path) + data.byteInputStream().use { it.copyTo(jos) } + } + return this + } + + fun build() { + jos.close() + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt new file mode 100644 index 000000000..51d740e5d --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt @@ -0,0 +1,63 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo + +import com.github.jengelman.gradle.plugins.shadow.util.HashUtil +import java.io.File +import java.io.OutputStream +import java.io.Writer +import java.math.BigInteger + +abstract class AbstractModule { + + protected abstract fun onPublish(file: File) + + protected fun publishWithWriter(file: File, action: (Writer) -> Unit) { + publishCommon(file) { it.writer().use(action) } + } + + protected fun publishWithStream(file: File, action: (OutputStream) -> Unit) { + publishCommon(file) { it.outputStream().use(action) } + } + + private fun publishCommon(file: File, action: (File) -> Unit) { + val hashBefore = if (file.exists()) getHash(file, "sha1") else null + val tempFile = file.resolveSibling("${file.name}.tmp") + action(tempFile) + + val hashAfter = getHash(tempFile, "sha1") + if (hashAfter == hashBefore) { + // Already published + return + } + + check(!file.exists() || file.delete()) + check(tempFile.renameTo(file)) + onPublish(file) + } + + companion object { + @JvmStatic + fun sha1File(file: File): File { + return hashFile(file, "sha1", 40) + } + + @JvmStatic + fun md5File(file: File): File { + return hashFile(file, "md5", 32) + } + + private fun hashFile(file: File, algorithm: String, len: Int): File { + val hashFile = getHashFile(file, algorithm) + val hash = getHash(file, algorithm) + hashFile.writeText("$hash${len}x") + return hashFile + } + + private fun getHashFile(file: File, algorithm: String): File { + return File(file.parentFile, "${file.name}.$algorithm") + } + + private fun getHash(file: File, algorithm: String): BigInteger { + return HashUtil.createHash(file, algorithm.uppercase()).digest + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt new file mode 100644 index 000000000..b77d25886 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt @@ -0,0 +1,22 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import java.io.File + +interface MavenModule { + /** + * Publishes the `pom.xml` plus main artifact, plus any additional artifacts for this module. + * Publishes only those artifacts whose content has changed since the last call to `publish()`. + */ + fun publish(): MavenModule + + /** + * Publishes the `pom.xml` only + */ + fun publishPom(): MavenModule + + fun dependsOn(groupId: String, artifactId: String, version: String): MavenModule + + val pomFile: File + + val metaDataFile: File +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt new file mode 100644 index 000000000..a128e07f5 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt @@ -0,0 +1,14 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import java.net.URI + +/** + * A fixture for dealing with Maven repositories. + */ +interface MavenRepository { + val uri: URI + + fun module(groupId: String, artifactId: String): MavenModule + + fun module(groupId: String, artifactId: String, version: String): MavenModule +} From 126938b6b382b15a9f353f5bb6a42916d45c2959 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 12 Dec 2024 02:32:27 +0000 Subject: [PATCH 098/941] Update dependency androidx.lint:lint-gradle to v1.0.0-alpha03 (#1101) * Update dependency androidx.lint:lint-gradle to v1.0.0-alpha03 * Use org.gradle.api.Project.files(java.lang.Object...) * Update baseline --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- gradle/libs.versions.toml | 2 +- lint-baseline.xml | 12 ++++++------ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5b57ad69c..d2ed7a632 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" -androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha02" +androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha03" assertk-lint = "com.jzbrooks:assertk-lint:1.3.0" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" diff --git a/lint-baseline.xml b/lint-baseline.xml index e8f9336a9..6f209b0af 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -1,5 +1,5 @@ - + @@ -118,7 +118,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,7 +129,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +140,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -151,7 +151,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> 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 f688b1eec..ab83f8e66 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 @@ -82,7 +82,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( @Suppress("EagerGradleConfiguration") shadow.manifest.inheritFrom(jarTask.get().manifest) val attrProvider = jarTask.map { it.manifest.attributes["Class-Path"]?.toString().orEmpty() } - val files = project.objects.fileCollection().from(shadowConfiguration) + val files = project.files(shadowConfiguration) shadow.doFirst { if (!files.isEmpty) { val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } From 7cd24a104da99c51d66d466d41cbbf8e4d6fa1e4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 12 Dec 2024 06:13:33 -0500 Subject: [PATCH 099/941] Migrate all ListProperty usages to SetProperty (#1103) --- api/shadow.api | 30 +++++++++---------- src/docs/changes/README.md | 5 ++++ .../plugins/shadow/impl/RelocatorRemapper.kt | 2 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 12 ++++---- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 12 ++++---- .../ManifestAppenderTransformer.kt | 6 ++-- .../transformers/PropertiesFileTransformer.kt | 4 +-- .../shadow/transformers/TransformerContext.kt | 6 ++-- .../Log4j2PluginsCacheFileTransformerTest.kt | 2 +- 9 files changed, 42 insertions(+), 37 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index c73a61e14..e9bb98b26 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -83,7 +83,7 @@ public class com/github/jengelman/gradle/plugins/shadow/ShadowStats { } public class com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper : org/objectweb/asm/commons/Remapper { - public fun (Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public fun (Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V public fun hasRelocators ()Z public fun map (Ljava/lang/String;)Ljava/lang/String; public fun mapPath (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath;)Ljava/lang/String; @@ -245,7 +245,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Co public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; - public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Lorg/gradle/api/tasks/util/PatternSet;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ZZ)V + public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lorg/gradle/api/tasks/util/PatternSet;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ZZ)V public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; } @@ -302,7 +302,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public fun getApiJars ()Lorg/gradle/api/file/ConfigurableFileCollection; - public fun getConfigurations ()Lorg/gradle/api/provider/ListProperty; + public fun getConfigurations ()Lorg/gradle/api/provider/SetProperty; public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; public fun getEnableRelocation ()Lorg/gradle/api/provider/Property; public fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; @@ -311,12 +311,12 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; public fun getMinimizeJar ()Lorg/gradle/api/provider/Property; public fun getRelocationPrefix ()Lorg/gradle/api/provider/Property; - public fun getRelocators ()Lorg/gradle/api/provider/ListProperty; + public fun getRelocators ()Lorg/gradle/api/provider/SetProperty; protected fun getRootPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; public fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; public fun getToMinimize ()Lorg/gradle/api/file/ConfigurableFileCollection; - public fun getTransformers ()Lorg/gradle/api/provider/ListProperty; + public fun getTransformers ()Lorg/gradle/api/provider/SetProperty; public fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; @@ -465,7 +465,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestApp public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun append (Ljava/lang/String;Ljava/lang/Comparable;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer; public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public fun getAttributes ()Lorg/gradle/api/provider/ListProperty; + public fun getAttributes ()Lorg/gradle/api/provider/SetProperty; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V @@ -501,7 +501,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesF public fun getMergeSeparator ()Lorg/gradle/api/provider/Property; public fun getMergeStrategy ()Lorg/gradle/api/provider/Property; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; - public fun getPaths ()Lorg/gradle/api/provider/ListProperty; + public fun getPaths ()Lorg/gradle/api/provider/SetProperty; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V @@ -574,21 +574,21 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion; public fun (Ljava/lang/String;Ljava/io/InputStream;)V - public fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;)V - public fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V - public synthetic fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;)V + public fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V + public synthetic fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/io/InputStream; - public final fun component3 ()Ljava/util/List; + public final fun component3 ()Ljava/util/Set; public final fun component4 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; - public final fun copy (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; - public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;Ljava/lang/String;Ljava/io/InputStream;Ljava/util/List;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public final fun copy (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; public fun equals (Ljava/lang/Object;)Z public static final fun getEntryTimestamp (ZJ)J public final fun getInputStream ()Ljava/io/InputStream; public final fun getPath ()Ljava/lang/String; - public final fun getRelocators ()Ljava/util/List; + public final fun getRelocators ()Ljava/util/Set; public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -599,7 +599,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans public final fun build ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; public final fun inputStream (Ljava/io/InputStream;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; public final fun path (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; - public final fun relocators (Ljava/util/List;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; + public final fun relocators (Ljava/util/Set;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; public final fun stats (Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; } diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 63dc6b7f8..96e83d9d9 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -7,6 +7,11 @@ - Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) +**Changed** + +- **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) + Some public `List` parameters are also changed to `Set`. + ## [v9.0.0-beta4] (2024-12-06) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt index 92ddede28..9337a3643 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt @@ -15,7 +15,7 @@ import org.objectweb.asm.commons.Remapper * @author John Engelman */ public open class RelocatorRemapper( - private val relocators: List, + private val relocators: Set, private val stats: ShadowStats, ) : Remapper() { private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 657b1ab17..b16de7ec8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -44,8 +44,8 @@ public open class ShadowCopyAction internal constructor( private val compressor: ZipCompressor, private val documentationRegistry: DocumentationRegistry, private val encoding: String?, - private val transformers: List, - private val relocators: List, + private val transformers: Set, + private val relocators: Set, private val patternSet: PatternSet, private val stats: ShadowStats, private val preserveFileTimestamps: Boolean, @@ -58,8 +58,8 @@ public open class ShadowCopyAction internal constructor( compressor: ZipCompressor, documentationRegistry: DocumentationRegistry, encoding: String?, - transformers: List, - relocators: List, + transformers: Set, + relocators: Set, patternSet: PatternSet, stats: ShadowStats, preserveFileTimestamps: Boolean, @@ -166,8 +166,8 @@ public open class ShadowCopyAction internal constructor( private inner class StreamAction( private val zipOutStr: ZipOutputStream, encoding: String?, - private val transformers: List, - private val relocators: List, + private val transformers: Set, + private val relocators: Set, private val patternSet: PatternSet, private val unused: Set, private val stats: ShadowStats, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 8640650a5..b31bcc10c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -31,8 +31,8 @@ import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.DefaultCopySpec -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Classpath import org.gradle.api.tasks.Input @@ -113,14 +113,14 @@ public abstract class ShadowJar : } @get:Nested - public open val transformers: ListProperty = objectFactory.listProperty(Transformer::class.java) + public open val transformers: SetProperty = objectFactory.setProperty(Transformer::class.java) @get:Nested - public open val relocators: ListProperty = objectFactory.listProperty(Relocator::class.java) + public open val relocators: SetProperty = objectFactory.setProperty(Relocator::class.java) @get:Classpath @get:Optional - public open val configurations: ListProperty = objectFactory.listProperty(Configuration::class.java) + public open val configurations: SetProperty = objectFactory.setProperty(Configuration::class.java) @get:Internal public open val dependencyFilter: Property = @@ -294,8 +294,8 @@ public abstract class ShadowJar : if (!enableRelocation.get()) return emptyList() val prefix = relocationPrefix.get() - // Must cast configurations to List to fix type mismatch in runtime. - return (configurations.get() as List).flatMap { configuration -> + // Must cast configurations to Set to fix type mismatch in runtime. + return (configurations.get() as Set).flatMap { configuration -> configuration.files.flatMap { file -> JarFile(file).use { jarFile -> jarFile.entries().toList() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 473c81568..230058cfd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -8,7 +8,7 @@ import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input import org.slf4j.LoggerFactory @@ -27,8 +27,8 @@ public open class ManifestAppenderTransformer @Inject constructor( @Suppress("UNCHECKED_CAST") @get:Input - public open val attributes: ListProperty>> = - objectFactory.listProperty(Pair::class.java) as ListProperty>> + public open val attributes: SetProperty>> = + objectFactory.setProperty(Pair::class.java) as SetProperty>> override fun canTransformResource(element: FileTreeElement): Boolean { return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 1e3a80a5d..57be7a405 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -14,9 +14,9 @@ import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.ListProperty import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal @@ -107,7 +107,7 @@ public open class PropertiesFileTransformer @Inject constructor( internal val propertiesEntries = mutableMapOf() @get:Input - public open val paths: ListProperty = objectFactory.listProperty(String::class.java) + public open val paths: SetProperty = objectFactory.setProperty(String::class.java) @Suppress("UNCHECKED_CAST") @get:Input diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt index 3bb9b1351..cefd226a0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -8,18 +8,18 @@ import java.io.InputStream public data class TransformerContext @JvmOverloads constructor( val path: String, val inputStream: InputStream, - val relocators: List = emptyList(), + val relocators: Set = emptySet(), val stats: ShadowStats = ShadowStats(), ) { public class Builder { private var path = "" private var inputStream: InputStream? = null - private var relocators = emptyList() + private var relocators = emptySet() private var stats = ShadowStats() public fun path(path: String): Builder = apply { this.path = path } public fun inputStream(inputStream: InputStream): Builder = apply { this.inputStream = inputStream } - public fun relocators(relocators: List): Builder = apply { this.relocators = relocators } + public fun relocators(relocators: Set): Builder = apply { this.relocators = relocators } public fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } public fun build(): TransformerContext = TransformerContext( path = path, diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index 1b933940d..f411b33a4 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -47,7 +47,7 @@ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest Date: Fri, 13 Dec 2024 00:50:27 +0800 Subject: [PATCH 100/941] Update plugin com.gradle.develocity to v3.19 (#1104) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 69acce744..774c947d5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "3.18.2" + id("com.gradle.develocity") version "3.19" } develocity { From 099865d50d78e25efe3937bf26d050ecadd891ba Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 12 Dec 2024 22:34:16 -0500 Subject: [PATCH 101/941] Compat deprecated getDependencyProject usage (#1106) --- .../gradle/plugins/shadow/internal/GradleCompat.kt | 13 +++++++++++++ .../gradle/plugins/shadow/internal/UnusedTracker.kt | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index a4a3b5dae..cf60ff126 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.internal import org.gradle.api.Project import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.model.ObjectFactory import org.gradle.api.plugins.JavaPlugin @@ -34,3 +35,15 @@ internal fun ConfigurableFileCollection.conventionCompat(vararg paths: Any): Con this } } + +/** + * TODO: this could be removed after bumping the min Gradle requirement to 8.11 or above. + */ +internal fun ProjectDependency.dependencyProjectCompat(project: Project): Project { + return if (GradleVersion.current() >= GradleVersion.version("8.11")) { + project.project(path) + } else { + @Suppress("DEPRECATION") + dependencyProject + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index 78a02537a..436bfcea1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -56,7 +56,7 @@ internal class UnusedTracker private constructor( apiDependencies.forEach { dep -> when (dep) { is ProjectDependency -> { - apiJars.addAll(getApiJarsFromProject(dep.dependencyProject)) + apiJars.addAll(getApiJarsFromProject(dep.dependencyProjectCompat(project))) addJar(runtimeConfiguration, dep, apiJars) } is SelfResolvingDependency -> { From eb6171930fc0c2a04f12e78b7bdc5269e7320699 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Dec 2024 13:22:53 +0000 Subject: [PATCH 102/941] Update dependency org.apache.logging.log4j:log4j-core to v2.24.3 (#1107) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d2ed7a632..917c51087 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.18.0" apache-commonsLang = "org.apache.commons:commons-lang3:3.17.0" -apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.2" +apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" asm = "org.ow2.asm:asm-commons:9.7.1" jdependency = "org.vafer:jdependency:2.11" jdom2 = "org.jdom:jdom2:2.0.6.1" From 0bd16c36e02a13739624f229d003182d9f47a537 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 16 Dec 2024 00:10:25 -0500 Subject: [PATCH 103/941] Simplify property initializations (#1102) --- .../plugins/shadow/internal/GradleCompat.kt | 51 +++++++++++++--- .../gradle/plugins/shadow/internal/Utils.kt | 5 -- .../shadow/relocation/SimpleRelocator.kt | 5 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 61 +++++++++---------- .../ManifestAppenderTransformer.kt | 5 +- .../ManifestResourceTransformer.kt | 4 +- .../transformers/PropertiesFileTransformer.kt | 12 ++-- 7 files changed, 85 insertions(+), 58 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index cf60ff126..0061d60e7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -6,7 +6,10 @@ import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.model.ObjectFactory import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.provider.SetProperty import org.gradle.util.GradleVersion /** @@ -18,21 +21,53 @@ internal inline val Project.runtimeConfiguration: Configuration ?: configurations.getByName("runtime") } -internal inline fun ObjectFactory.property(defaultValue: T? = null): Property { - return property(T::class.java).apply { - if (defaultValue != null) convention(defaultValue) +internal inline fun ObjectFactory.property( + defaultValue: Any? = null, +): Property = property(V::class.java).apply { + defaultValue ?: return@apply + if (defaultValue is Provider<*>) { + @Suppress("UNCHECKED_CAST") + convention(defaultValue as Provider) + } else { + convention(defaultValue as V) + } +} + +@Suppress("UNCHECKED_CAST") +internal inline fun ObjectFactory.setProperty( + defaultValue: Any? = null, +): SetProperty = setProperty(V::class.java).apply { + defaultValue ?: return@apply + if (defaultValue is Provider<*>) { + convention(defaultValue as Provider>) + } else { + convention(defaultValue as Iterable) + } +} + +@Suppress("UNCHECKED_CAST") +internal inline fun ObjectFactory.mapProperty( + defaultValue: Any? = null, +): MapProperty = mapProperty(String::class.java, V::class.java).apply { + defaultValue ?: return@apply + if (defaultValue is Provider<*>) { + convention(defaultValue as Provider>) + } else { + convention(defaultValue as Map) } } /** * TODO: this could be removed after bumping the min Gradle requirement to 8.8 or above. */ -internal fun ConfigurableFileCollection.conventionCompat(vararg paths: Any): ConfigurableFileCollection { - return if (GradleVersion.current() >= GradleVersion.version("8.8")) { - convention(paths) +internal inline fun ObjectFactory.fileCollection( + path: () -> Any, +): ConfigurableFileCollection = fileCollection().apply { + @Suppress("UnstableApiUsage") + if (GradleVersion.current() >= GradleVersion.version("8.8")) { + convention(path()) } else { - setFrom(paths) - this + setFrom(path()) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 4ec58607b..daac5eae8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -26,11 +26,6 @@ internal fun createDefaultFileTreeElement( return DefaultFileTreeElement(file, relativePath, chmod, stat) } -@Suppress("NOTHING_TO_INLINE") -internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy { - return lazy(LazyThreadSafetyMode.NONE, initializer) -} - internal fun Properties.inputStream( charset: Charset = Charsets.ISO_8859_1, comments: String = "", diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 78837b622..9057eedc2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.relocation +import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import java.util.regex.Pattern import org.codehaus.plexus.util.SelectorUtils import org.gradle.api.model.ObjectFactory @@ -30,10 +31,10 @@ public open class SimpleRelocator @JvmOverloads constructor( private val sourcePathExcludes = mutableSetOf() @get:Input - public val includes: SetProperty = objectFactory.setProperty(String::class.java) + public val includes: SetProperty = objectFactory.setProperty() @get:Input - public val excludes: SetProperty = objectFactory.setProperty(String::class.java) + public val excludes: SetProperty = objectFactory.setProperty() init { if (rawString) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index b31bcc10c..77fe69a6c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -8,9 +8,9 @@ import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor -import com.github.jengelman.gradle.plugins.shadow.internal.conventionCompat +import com.github.jengelman.gradle.plugins.shadow.internal.fileCollection import com.github.jengelman.gradle.plugins.shadow.internal.property -import com.github.jengelman.gradle.plugins.shadow.internal.unsafeLazy +import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator @@ -20,6 +20,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create +import java.io.File import java.util.jar.JarFile import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action @@ -68,32 +69,37 @@ public abstract class ShadowJar : @get:Internal override val stats: ShadowStats = ShadowStats() + /** + * Minimize the jar by removing unused classes. + * + * Defaults to `false`. + */ + @get:Input + public open val minimizeJar: Property = objectFactory.property(false) + @get:Classpath - public open val toMinimize: ConfigurableFileCollection by unsafeLazy { - objectFactory.fileCollection().apply { - if (minimizeJar.get()) { - conventionCompat(dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) - } + public open val toMinimize: ConfigurableFileCollection = objectFactory.fileCollection { + minimizeJar.map { + if (it) (dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) else emptySet() } } @get:Classpath - public open val apiJars: ConfigurableFileCollection by unsafeLazy { - objectFactory.fileCollection().apply { - if (minimizeJar.get()) { - conventionCompat(UnusedTracker.getApiJarsFromProject(project)) - } + public open val apiJars: ConfigurableFileCollection = objectFactory.fileCollection { + minimizeJar.map { + if (it) UnusedTracker.getApiJarsFromProject(project) else emptySet() } } @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) - public open val sourceSetsClassesDirs: ConfigurableFileCollection by unsafeLazy { - objectFactory.fileCollection().apply { - if (minimizeJar.get()) { - project.extensions.getByType(SourceSetContainer::class.java).forEach { sourceSet -> - from(sourceSet.output.classesDirs.filter { it.isDirectory }) - } + public open val sourceSetsClassesDirs: ConfigurableFileCollection = objectFactory.fileCollection { + minimizeJar.map { + if (it) { + project.extensions.getByType(SourceSetContainer::class.java) + .map { sourceSet -> sourceSet.output.classesDirs.filter(File::isDirectory) } + } else { + emptySet() } } } @@ -113,22 +119,23 @@ public abstract class ShadowJar : } @get:Nested - public open val transformers: SetProperty = objectFactory.setProperty(Transformer::class.java) + public open val transformers: SetProperty = objectFactory.setProperty() @get:Nested - public open val relocators: SetProperty = objectFactory.setProperty(Relocator::class.java) + public open val relocators: SetProperty = objectFactory.setProperty() @get:Classpath @get:Optional - public open val configurations: SetProperty = objectFactory.setProperty(Configuration::class.java) + public open val configurations: SetProperty = objectFactory.setProperty() @get:Internal public open val dependencyFilter: Property = objectFactory.property(DefaultDependencyFilter(project)) @get:Classpath - public open val includedDependencies: ConfigurableFileCollection = objectFactory.fileCollection() - .conventionCompat(dependencyFilter.map { it.resolve(configurations.get()) }) + public open val includedDependencies: ConfigurableFileCollection = objectFactory.fileCollection { + dependencyFilter.zip(configurations) { df, cs -> df.resolve(cs) } + } /** * Enable relocation of packages in the jar. @@ -146,14 +153,6 @@ public abstract class ShadowJar : @get:Input public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) - /** - * Minimize the jar by removing unused classes. - * - * Defaults to `false`. - */ - @get:Input - public open val minimizeJar: Property = objectFactory.property(false) - @Internal override fun getManifest(): InheritManifest = super.manifest as InheritManifest diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 230058cfd..f8bf89614 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import java.io.ByteArrayOutputStream import java.io.IOException import java.util.jar.JarFile.MANIFEST_NAME @@ -25,10 +26,8 @@ public open class ManifestAppenderTransformer @Inject constructor( ) : Transformer { private var manifestContents = ByteArray(0) - @Suppress("UNCHECKED_CAST") @get:Input - public open val attributes: SetProperty>> = - objectFactory.setProperty(Pair::class.java) as SetProperty>> + public open val attributes: SetProperty>> = objectFactory.setProperty() override fun canTransformResource(element: FileTreeElement): Boolean { return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index c63fde158..1de0158ef 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp import java.io.IOException @@ -39,8 +40,7 @@ public open class ManifestResourceTransformer @Inject constructor( @get:Optional @get:Input - public open val manifestEntries: MapProperty = - objectFactory.mapProperty(String::class.java, Attributes::class.java) + public open val manifestEntries: MapProperty = objectFactory.mapProperty() override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 57be7a405..2950715ae 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -2,7 +2,9 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties import com.github.jengelman.gradle.plugins.shadow.internal.inputStream +import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property +import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import groovy.lang.Closure import groovy.lang.Closure.IDENTITY @@ -107,12 +109,10 @@ public open class PropertiesFileTransformer @Inject constructor( internal val propertiesEntries = mutableMapOf() @get:Input - public open val paths: SetProperty = objectFactory.setProperty(String::class.java) + public open val paths: SetProperty = objectFactory.setProperty() - @Suppress("UNCHECKED_CAST") @get:Input - public open val mappings: MapProperty> = - objectFactory.mapProperty(String::class.java, Map::class.java) as MapProperty> + public open val mappings: MapProperty> = objectFactory.mapProperty() @get:Input public open val mergeStrategy: Property = objectFactory.property(MergeStrategy.First) @@ -123,10 +123,8 @@ public open class PropertiesFileTransformer @Inject constructor( @get:Input public open val charsetName: Property = objectFactory.property(Charsets.ISO_8859_1.name()) - @Suppress("UNCHECKED_CAST") @get:Internal - public open val keyTransformer: Property> = - objectFactory.property(IDENTITY) as Property> + public open val keyTransformer: Property> = objectFactory.property(IDENTITY) override fun canTransformResource(element: FileTreeElement): Boolean { val mappings = mappings.get() From 519a1510c1282943790d1270c3b329cebbbb5320 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 05:54:58 +0800 Subject: [PATCH 104/941] Update dependency org.junit:junit-bom to v5.11.4 (#1108) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 917c51087..9b0e5f650 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,7 +16,7 @@ assertk-lint = "com.jzbrooks:assertk-lint:1.3.0" ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" spock = "org.spockframework:spock-core:2.3-groovy-3.0" -junit-bom = "org.junit:junit-bom:5.11.3" +junit-bom = "org.junit:junit-bom:5.11.4" junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" } junit-platformLauncher = { module = "org.junit.platform:junit-platform-launcher" } assertk = "com.willowtreeapps.assertk:assertk:0.28.1" From df77d90d88f81eaed23a10fbc2448dc4d72c8abd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 17 Dec 2024 09:08:51 +0800 Subject: [PATCH 105/941] Update dependency org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin to v2 (#1109) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build-logic/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 997d037f7..7fe85ba88 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -10,7 +10,7 @@ repositories { dependencies { implementation("com.gradle.publish:plugin-publish-plugin:1.3.0") implementation("com.vanniktech:gradle-maven-publish-plugin:0.30.0") - implementation("org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin:1.9.20") + implementation("org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin:2.0.0") implementation("org.ajoberstar.git-publish:gradle-git-publish:5.1.0") implementation("com.github.node-gradle:gradle-node-plugin:7.1.0") } From 2609fb390b2da8e42a444dbe3cee1624a9c0a40c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 18 Dec 2024 20:19:50 -0500 Subject: [PATCH 106/941] Migrate to Dokka's new coordinate (#1110) https://kotlinlang.org/docs/dokka-migration.html#multi-module-projects-without-convention-plugins --- build-logic/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 7fe85ba88..2a9ce6c3f 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -10,7 +10,7 @@ repositories { dependencies { implementation("com.gradle.publish:plugin-publish-plugin:1.3.0") implementation("com.vanniktech:gradle-maven-publish-plugin:0.30.0") - implementation("org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin:2.0.0") + implementation("org.jetbrains.dokka:dokka-gradle-plugin:2.0.0") implementation("org.ajoberstar.git-publish:gradle-git-publish:5.1.0") implementation("com.github.node-gradle:gradle-node-plugin:7.1.0") } From 65be956d9fc0cb355353302c673fd07237c02883 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 18 Dec 2024 20:31:05 -0500 Subject: [PATCH 107/941] Extract build-logic dependencies into version catalog (#1111) --- build-logic/build.gradle.kts | 15 +++++---------- build-logic/settings.gradle.kts | 12 ++++++++++++ gradle/libs.versions.toml | 6 ++++++ 3 files changed, 23 insertions(+), 10 deletions(-) create mode 100644 build-logic/settings.gradle.kts diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index 2a9ce6c3f..c2c603c1e 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -2,15 +2,10 @@ plugins { `kotlin-dsl` } -repositories { - mavenCentral() - gradlePluginPortal() -} - dependencies { - implementation("com.gradle.publish:plugin-publish-plugin:1.3.0") - implementation("com.vanniktech:gradle-maven-publish-plugin:0.30.0") - implementation("org.jetbrains.dokka:dokka-gradle-plugin:2.0.0") - implementation("org.ajoberstar.git-publish:gradle-git-publish:5.1.0") - implementation("com.github.node-gradle:gradle-node-plugin:7.1.0") + implementation(libs.pluginPublish) + implementation(libs.mavenPublish) + implementation(libs.gitPublish) + implementation(libs.node) + implementation(libs.jetbrains.dokka) } diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 000000000..8f0b5dc0b --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,12 @@ +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } + + repositories { + mavenCentral() + gradlePluginPortal() + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9b0e5f650..a0cc316d5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,6 +10,12 @@ plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" +pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.0" +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" + androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha03" assertk-lint = "com.jzbrooks:assertk-lint:1.3.0" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. From 0c4da674551f90dfe6ba635db55298fdd76a4d63 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:14:26 +0800 Subject: [PATCH 108/941] Update plugin jetbrains-bcv to v0.17.0 (#1112) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a0cc316d5..faebe4fda 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,5 +30,5 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin = "org.jetbrains.kotlin.jvm:2.1.0" android-lint = "com.android.lint:8.7.3" -jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.16.3" +jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" spotless = "com.diffplug.spotless:7.0.0.BETA4" From 4ffb34f550dffedeea32fc5b54384f425a9eb87a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 21 Dec 2024 14:29:25 +0800 Subject: [PATCH 109/941] Update dependency gradle to v8.12 (#1113) * Update dependency gradle to v8.12 * Space-assignment syntax in Groovy DSL has been deprecated. This is scheduled to be removed in Gradle 10.0. Consult the upgrading guide for further information: https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#groovy_space_assignment_syntax --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 3 +- .../shadow/ConfigurationCacheSpec.groovy | 6 +-- .../plugins/shadow/PublishingSpec.groovy | 10 ++-- .../plugins/shadow/RelocationSpec.groovy | 6 +-- .../plugins/shadow/ShadowPluginSpec.groovy | 54 +++++++++---------- .../caching/MinimizationCachingSpec.groovy | 6 +-- .../shadow/util/PluginSpecification.groovy | 2 +- 8 files changed, 44 insertions(+), 45 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e2847c820..cea7a793a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f5feea6d6..f3b75f3b0 100755 --- a/gradlew +++ b/gradlew @@ -86,8 +86,7 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy index af3a000ea..9bb5ef2a0 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy @@ -93,7 +93,7 @@ class ConfigurationCacheSpec extends PluginSpecification { """.stripIndent() file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -112,7 +112,7 @@ class ConfigurationCacheSpec extends PluginSpecification { } } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -151,7 +151,7 @@ class ConfigurationCacheSpec extends PluginSpecification { apply plugin: 'java' apply plugin: 'com.gradleup.shadow' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation "junit:junit:3.8.2" } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy index ce60a5e12..e6e399b01 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy @@ -50,7 +50,7 @@ class PublishingSpec extends PluginSpecification { } repositories { maven { - url "${publishingRepo.uri}" + url = "${publishingRepo.uri}" } } } @@ -112,7 +112,7 @@ class PublishingSpec extends PluginSpecification { } repositories { maven { - url "${publishingRepo.uri}" + url = "${publishingRepo.uri}" } } } @@ -150,11 +150,11 @@ class PublishingSpec extends PluginSpecification { version = "1.0" group = 'shadow' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } publishing { repositories { maven { - url "${publishingRepo.uri}" + url = "${publishingRepo.uri}" } } } @@ -263,7 +263,7 @@ class PublishingSpec extends PluginSpecification { } repositories { maven { - url "${publishingRepo.uri}" + url = "${publishingRepo.uri}" } } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy index 2915cc8f4..a72008197 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy @@ -186,7 +186,7 @@ class RelocationSpec extends PluginSpecification { file('core/build.gradle') << """ apply plugin: 'java-library' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { api 'junit:junit:3.8.2' } """.stripIndent() @@ -205,7 +205,7 @@ class RelocationSpec extends PluginSpecification { apply plugin: 'java' apply plugin: 'com.gradleup.shadow' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':core') } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -296,7 +296,7 @@ class RelocationSpec extends PluginSpecification { repositories { mavenCentral() maven { - url 'https://repository.mapr.com/nexus/content/groups/mapr-public/releases' + url = 'https://repository.mapr.com/nexus/content/groups/mapr-public/releases' } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index f57b069fb..f214975d5 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -172,7 +172,7 @@ class ShadowPluginSpec extends PluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -188,7 +188,7 @@ class ShadowPluginSpec extends PluginSpecification { apply plugin: 'java' apply plugin: 'com.gradleup.shadow' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -224,7 +224,7 @@ class ShadowPluginSpec extends PluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -246,7 +246,7 @@ class ShadowPluginSpec extends PluginSpecification { minimize() } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -282,7 +282,7 @@ class ShadowPluginSpec extends PluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -301,7 +301,7 @@ class ShadowPluginSpec extends PluginSpecification { } } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -336,7 +336,7 @@ class ShadowPluginSpec extends PluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } """.stripIndent() file('server/src/main/java/server/Server.java') << """ @@ -354,7 +354,7 @@ class ShadowPluginSpec extends PluginSpecification { } } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -390,7 +390,7 @@ class ShadowPluginSpec extends PluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -409,7 +409,7 @@ class ShadowPluginSpec extends PluginSpecification { } } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -443,7 +443,7 @@ class ShadowPluginSpec extends PluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -462,7 +462,7 @@ class ShadowPluginSpec extends PluginSpecification { } } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -503,7 +503,7 @@ class ShadowPluginSpec extends PluginSpecification { file('lib/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } """.stripIndent() file('api/src/main/java/api/Entity.java') << """ @@ -519,7 +519,7 @@ class ShadowPluginSpec extends PluginSpecification { file('api/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' implementation project(':lib') @@ -540,7 +540,7 @@ class ShadowPluginSpec extends PluginSpecification { minimize() } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { api project(':api') } """.stripIndent() @@ -582,7 +582,7 @@ class ShadowPluginSpec extends PluginSpecification { file('lib/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } """.stripIndent() file('api/src/main/java/api/Entity.java') << """ @@ -598,7 +598,7 @@ class ShadowPluginSpec extends PluginSpecification { file('api/build.gradle') << """ apply plugin: 'java-library' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { api project(':lib') } """.stripIndent() @@ -616,7 +616,7 @@ class ShadowPluginSpec extends PluginSpecification { minimize() } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { api project(':api') } """.stripIndent() @@ -650,7 +650,7 @@ class ShadowPluginSpec extends PluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' apply plugin: 'com.gradleup.shadow' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -670,7 +670,7 @@ class ShadowPluginSpec extends PluginSpecification { file('server/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(path: ':client', configuration: 'shadow') } """.stripIndent() @@ -707,7 +707,7 @@ class ShadowPluginSpec extends PluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' apply plugin: 'com.gradleup.shadow' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -728,7 +728,7 @@ class ShadowPluginSpec extends PluginSpecification { apply plugin: 'java' apply plugin: 'com.gradleup.shadow' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(path: ':client', configuration: 'shadow') } """.stripIndent() @@ -979,7 +979,7 @@ class ShadowPluginSpec extends PluginSpecification { dependencies { shadow 'junit:junit:3.8.2' } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - zip64 true + zip64 = true entryCompression = org.gradle.api.tasks.bundling.ZipEntryCompression.STORED } """.stripIndent() @@ -1001,7 +1001,7 @@ class ShadowPluginSpec extends PluginSpecification { file('lib/build.gradle') << """ apply plugin: 'java' version = '1.0' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } """.stripIndent() file('api/src/main/java/api/UnusedEntity.java') << """ @@ -1012,7 +1012,7 @@ class ShadowPluginSpec extends PluginSpecification { file('api/build.gradle') << """ apply plugin: 'java' version = '1.0' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' implementation project(':lib') @@ -1024,7 +1024,7 @@ class ShadowPluginSpec extends PluginSpecification { apply plugin: 'com.gradleup.shadow' version = '1.0' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { api project(':api') } shadowJar.minimize() @@ -1123,7 +1123,7 @@ class ShadowPluginSpec extends PluginSpecification { buildscript { repositories { maven { - url "https://maven.eveoh.nl/content/repositories/releases" + url = "https://maven.eveoh.nl/content/repositories/releases" } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy index 47a82125d..460fcc108 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy @@ -20,7 +20,7 @@ class MinimizationCachingSpec extends AbstractCachingSpec { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -33,7 +33,7 @@ class MinimizationCachingSpec extends AbstractCachingSpec { apply plugin: 'java' apply plugin: 'com.gradleup.shadow' - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -61,7 +61,7 @@ class MinimizationCachingSpec extends AbstractCachingSpec { } } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() assertShadowJarExecutes() diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy index 81678db82..780763716 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy @@ -47,7 +47,7 @@ abstract class PluginSpecification extends Specification { integTest } - repositories { maven { url "${repo.uri}" } } + repositories { maven { url = "${repo.uri}" } } """.stripIndent() } From 7694a665d2dc5cf20ac8939f2b1bda3e2afe67f8 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 21 Dec 2024 01:54:41 -0500 Subject: [PATCH 110/941] Replace deprecated SelfResolvingDependency with FileCollectionDependency (#1114) https://docs.gradle.org/8.12/userguide/upgrading_version_8.html#deprecate_self_resolving_dependency --- src/docs/changes/README.md | 1 + .../gradle/plugins/shadow/internal/UnusedTracker.kt | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 96e83d9d9..fbec6de81 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -11,6 +11,7 @@ - **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) Some public `List` parameters are also changed to `Set`. +- Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) ## [v9.0.0-beta4] (2024-12-06) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index 436bfcea1..099fe20f6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -4,8 +4,8 @@ import java.io.File import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.FileCollectionDependency import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.SelfResolvingDependency import org.gradle.api.file.FileCollection import org.gradle.api.tasks.InputFiles import org.vafer.jdependency.Clazzpath @@ -59,8 +59,8 @@ internal class UnusedTracker private constructor( apiJars.addAll(getApiJarsFromProject(dep.dependencyProjectCompat(project))) addJar(runtimeConfiguration, dep, apiJars) } - is SelfResolvingDependency -> { - apiJars.addAll(dep.resolve()) + is FileCollectionDependency -> { + apiJars.addAll(dep.files) } else -> { addJar(runtimeConfiguration, dep, apiJars) From 68b1818c6717dfe3fd9b0d302f1647a6f04ee9d4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 22 Dec 2024 23:21:03 +0800 Subject: [PATCH 111/941] Fix the comment for ShadowExtension#component --- .../github/jengelman/gradle/plugins/shadow/ShadowExtension.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index 924148c32..eb626073b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -7,7 +7,7 @@ import org.gradle.api.publish.maven.MavenPublication public abstract class ShadowExtension(project: Project) { private val components = project.components - @Deprecated("configure publication using component.shadow directly.") + @Deprecated("Configure publication using `components.shadow` directly.") public fun component(publication: MavenPublication) { publication.from(components.findByName(ShadowBasePlugin.COMPONENT_NAME)) } From 9f886d690e487e580bdaae54fe857dfd1efd150e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Dec 2024 07:53:29 +0800 Subject: [PATCH 112/941] Update dependency com.jzbrooks:assertk-lint to v1.4.0 (#1116) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index faebe4fda..7f27a236e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,7 +17,7 @@ jetbrains-dokka = "org.jetbrains.dokka:dokka-gradle-plugin:2.0.0" node = "com.github.node-gradle:gradle-node-plugin:7.1.0" androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha03" -assertk-lint = "com.jzbrooks:assertk-lint:1.3.0" +assertk-lint = "com.jzbrooks:assertk-lint:1.4.0" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" From a2c30ecc4371c9b1933751716dca744a1302fbf1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 25 Dec 2024 01:04:35 -0500 Subject: [PATCH 113/941] Convert functional test kits to Kotlin and NIO (#1087) * Migrate AbstractMavenModule to using Maven model APIs * Migrate MavenFileModule to using Maven model APIs * Use Dependency in AbstractMavenModule * Tweak dependsOn params * Replace getMetaDataFileContent with getMetaData * Replace publishWithWriter with publishWithStream * Convert AbstractMavenModule * Convert MavenFileModule * Convert MavenFileRepository * Update AppendableJar * Convert AppendableMavenFileModule * Convert AppendableMavenFileRepository * Rename PluginSpecification to BasePluginSpecification * Apply suggestions from code review * Cleanups * Simplify JarBuilder * Simplify HashUtil * Simplify AbstractModule * Mark rootDir public * Rename onPublish to postPublish * Migrate File usages to Path * Simplify publish and postPublish * Remove FileExtensions for Groovy * Seems we don't need escapedPath * We don't need the overload for module * Simplify AppendableMavenFileModule * Revert "Seems we don't need escapedPath" This reverts commit 7b1314f0165264ad3bf9bd00089cc68afced9d50. * Tweak AppendableJar --- build.gradle.kts | 6 + gradle/libs.versions.toml | 6 + .../plugins/shadow/ApplicationSpec.groovy | 3 +- ....groovy => BasePluginSpecification.groovy} | 19 +- .../shadow/ConfigurationCacheSpec.groovy | 4 +- .../ConfigureShadowRelocationSpec.groovy | 5 +- .../plugins/shadow/FilteringSpec.groovy | 3 +- .../plugins/shadow/PublishingSpec.groovy | 27 ++- .../plugins/shadow/RelocationSpec.groovy | 3 +- .../plugins/shadow/ShadowPluginSpec.groovy | 9 +- .../plugins/shadow/TransformerSpec.groovy | 81 +++---- .../shadow/caching/AbstractCachingSpec.groovy | 8 +- .../util/AppendableMavenFileModule.groovy | 60 ----- .../util/AppendableMavenFileRepository.groovy | 14 -- .../plugins/shadow/util/FileExtensions.groovy | 26 --- .../repo/maven/AbstractMavenModule.groovy | 218 ------------------ .../util/repo/maven/MavenFileModule.groovy | 46 ---- .../repo/maven/MavenFileRepository.groovy | 23 -- ...rg.codehaus.groovy.runtime.ExtensionModule | 3 - .../plugins/shadow/util/AppendableJar.kt | 26 ++- .../shadow/util/AppendableMavenFileModule.kt | 46 ++++ .../util/AppendableMavenFileRepository.kt | 11 + .../gradle/plugins/shadow/util/HashUtil.kt | 54 ----- .../gradle/plugins/shadow/util/HashValue.kt | 7 - .../gradle/plugins/shadow/util/JarBuilder.kt | 19 +- .../shadow/util/repo/AbstractModule.kt | 86 ++++--- .../util/repo/maven/AbstractMavenModule.kt | 184 +++++++++++++++ .../shadow/util/repo/maven/MavenFileModule.kt | 35 +++ .../util/repo/maven/MavenFileRepository.kt | 17 ++ .../shadow/util/repo/maven/MavenModule.kt | 6 +- .../shadow/util/repo/maven/MavenRepository.kt | 2 - 31 files changed, 456 insertions(+), 601 deletions(-) rename src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/{util/PluginSpecification.groovy => BasePluginSpecification.groovy} (87%) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy delete mode 100644 src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt diff --git a/build.gradle.kts b/build.gradle.kts index 42fbbde71..82187aafe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -54,6 +54,8 @@ val intiTestRuntimeOnly: Configuration by configurations.getting { val funcTest: SourceSet by sourceSets.creating val funcTestImplementation: Configuration by configurations.getting { extendsFrom(configurations.testImplementation.get()) + // TODO: this will be removed after we migrated all functional tests to Kotlin. + extendsFrom(intiTestImplementation) } val funcTestRuntimeOnly: Configuration by configurations.getting { extendsFrom(configurations.testRuntimeOnly.get()) @@ -88,6 +90,10 @@ dependencies { funcTestImplementation(sourceSets.main.get().output) funcTestImplementation(intiTest.output) + intiTestImplementation(libs.okio) + intiTestImplementation(libs.apache.maven.modelBuilder) + intiTestImplementation(libs.apache.maven.repositoryMetadata) + lintChecks(libs.androidx.gradlePluginLints) lintChecks(libs.assertk.lint) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7f27a236e..9112523a9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,20 @@ +[versions] +maven = "3.9.9" + [libraries] apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.18.0" apache-commonsLang = "org.apache.commons:commons-lang3:3.17.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" +apache-maven-modelBuilder = { module = "org.apache.maven:maven-model-builder", version.ref = "maven" } +apache-maven-repositoryMetadata = { module = "org.apache.maven:maven-repository-metadata", version.ref = "maven" } asm = "org.ow2.asm:asm-commons:9.7.1" jdependency = "org.vafer:jdependency:2.11" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" +okio = "com.squareup.okio:okio:3.9.1" pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.0" mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:0.30.0" diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy index ff6f72cb6..77c888a38 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import org.apache.tools.zip.ZipFile import org.gradle.testkit.runner.BuildResult import spock.lang.Issue @@ -8,7 +7,7 @@ import spock.lang.Issue import java.util.jar.Attributes import java.util.jar.JarFile -class ApplicationSpec extends PluginSpecification { +class ApplicationSpec extends BasePluginSpecification { def 'integration with application plugin'() { given: diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy similarity index 87% rename from src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy rename to src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy index 780763716..f3393d6ec 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/PluginSpecification.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy @@ -1,5 +1,7 @@ -package com.github.jengelman.gradle.plugins.shadow.util +package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.util.AppendableJar +import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository import org.codehaus.plexus.util.IOUtil import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner @@ -7,11 +9,12 @@ import spock.lang.Specification import spock.lang.TempDir import java.nio.file.Path +import java.nio.file.Paths import java.util.function.Function import java.util.jar.JarEntry import java.util.jar.JarFile -abstract class PluginSpecification extends Specification { +abstract class BasePluginSpecification extends Specification { @TempDir Path dir @@ -118,7 +121,7 @@ abstract class PluginSpecification extends Specification { } AppendableMavenFileRepository repo(String path = 'maven-repo') { - new AppendableMavenFileRepository(dir.resolve(path).toFile()) + new AppendableMavenFileRepository(dir.resolve(path)) } void assertJarFileContentsEqual(File f, String path, String contents) { @@ -161,7 +164,7 @@ abstract class PluginSpecification extends Specification { } AppendableJar buildJar(String path) { - return new AppendableJar(file(path)) + return new AppendableJar(file(path).toPath()) } protected File getOutput() { @@ -172,8 +175,8 @@ abstract class PluginSpecification extends Specification { getFile("build/libs/${name}") } - protected File getTestJar(String name = 'junit-3.8.2.jar') { - return new File(this.class.classLoader.getResource(name).toURI()) + protected Path getTestJar(String name = 'junit-3.8.2.jar') { + return Paths.get(this.class.classLoader.getResource(name).toURI()) } protected static File getTestKitDir() { @@ -183,4 +186,8 @@ abstract class PluginSpecification extends Specification { } return new File(gradleUserHome, "testkit") } + + protected static String escapedPath(Path path) { + return path.toString().replaceAll('\\\\', '\\\\\\\\') + } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy index 9bb5ef2a0..253db82bc 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy @@ -1,8 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification - -class ConfigurationCacheSpec extends PluginSpecification { +class ConfigurationCacheSpec extends BasePluginSpecification { @Override def setup() { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy index 2c5af9c15..654ef5800 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy @@ -1,9 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification - - -class ConfigureShadowRelocationSpec extends PluginSpecification { +class ConfigureShadowRelocationSpec extends BasePluginSpecification { def "auto relocate plugin dependencies"() { given: diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy index 970e33e8b..60dc6ac47 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy @@ -1,12 +1,11 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome import spock.lang.Ignore import spock.lang.Issue -class FilteringSpec extends PluginSpecification { +class FilteringSpec extends BasePluginSpecification { @Override def setup() { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy index e6e399b01..6f2616897 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy @@ -1,14 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import groovy.json.JsonSlurper import groovy.xml.XmlSlurper import org.gradle.api.attributes.Bundling import org.gradle.api.attributes.Usage import spock.lang.Issue -class PublishingSpec extends PluginSpecification { +class PublishingSpec extends BasePluginSpecification { AppendableMavenFileRepository publishingRepo @@ -60,14 +59,14 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').canonicalFile + File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').toFile().canonicalFile assert publishedFile.exists() and: contains(publishedFile, ['a.properties', 'a2.properties']) and: - File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').canonicalFile + File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile assert pom.exists() def contents = new XmlSlurper().parse(pom) @@ -128,7 +127,7 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext').canonicalFile + File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext').toFile().canonicalFile assert publishedFile.exists() } @@ -209,14 +208,14 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').canonicalFile + File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').toFile().canonicalFile assert publishedFile.exists() and: contains(publishedFile, ['a.properties', 'a2.properties']) and: - File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').canonicalFile + File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile assert pom.exists() def contents = new XmlSlurper().parse(pom) @@ -273,8 +272,8 @@ class PublishingSpec extends PluginSpecification { run('publish') then: - File mainJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.jar').canonicalFile - File shadowJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0-all.jar').canonicalFile + File mainJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.jar').toFile().canonicalFile + File shadowJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0-all.jar').toFile().canonicalFile assert mainJar.exists() assert shadowJar.exists() @@ -282,8 +281,8 @@ class PublishingSpec extends PluginSpecification { contains(shadowJar, ['a.properties', 'a2.properties']) and: "publishes both a POM file and a Gradle metadata file" - File pom = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.pom').canonicalFile - File gmm = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.module').canonicalFile + File pom = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.pom').toFile().canonicalFile + File gmm = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.module').toFile().canonicalFile pom.exists() gmm.exists() @@ -328,13 +327,13 @@ class PublishingSpec extends PluginSpecification { and: "verify shadow publication" assertions { - shadowJar = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0-all.jar').canonicalFile + shadowJar = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0-all.jar').toFile().canonicalFile assert shadowJar.exists() contains(shadowJar, ['a.properties', 'a2.properties']) } assertions { - pom = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.pom').canonicalFile + pom = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile assert pom.exists() pomContents = new XmlSlurper().parse(pom) assert pomContents.dependencies[0].dependency.size() == 1 @@ -351,7 +350,7 @@ class PublishingSpec extends PluginSpecification { } assertions { - gmm = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.module').canonicalFile + gmm = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.module').toFile().canonicalFile assert gmm.exists() gmmContents = new JsonSlurper().parse(gmm) assert gmmContents.variants.size() == 1 diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy index a72008197..244fbbcdf 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy @@ -1,13 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import spock.lang.Ignore import spock.lang.Issue import java.util.jar.Attributes import java.util.jar.JarFile -class RelocationSpec extends PluginSpecification { +class RelocationSpec extends BasePluginSpecification { @Issue('SHADOW-58') def "relocate dependency files"() { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index f214975d5..33d49dcc9 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -1,7 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import org.gradle.api.JavaVersion import org.gradle.api.Project import org.gradle.api.artifacts.Configuration @@ -17,7 +16,7 @@ import spock.lang.Unroll import java.util.jar.Attributes import java.util.jar.JarFile -class ShadowPluginSpec extends PluginSpecification { +class ShadowPluginSpec extends BasePluginSpecification { def 'apply plugin'() { given: @@ -63,7 +62,7 @@ class ShadowPluginSpec extends PluginSpecification { @Unroll def 'Compatible with Gradle #version'() { given: - File one = buildJar('one.jar').insertFile('META-INF/services/shadow.Shadow', + def one = buildJar('one.jar').insert('META-INF/services/shadow.Shadow', 'one # NOTE: No newline terminates this line/file').write() repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', @@ -1272,8 +1271,4 @@ class ShadowPluginSpec extends PluginSpecification { def jarFile = new JarFile(output("shadow-1.0-tests.jar")) assert jarFile.getEntry('junit') != null } - - private String escapedPath(File file) { - file.path.replaceAll('\\\\', '\\\\\\\\') - } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy index aac98bc18..1e3686980 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy @@ -4,27 +4,26 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransfor import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification import spock.lang.Issue import spock.lang.Unroll import java.util.jar.JarInputStream import java.util.jar.Manifest -class TransformerSpec extends PluginSpecification { +class TransformerSpec extends BasePluginSpecification { def 'service resource transformer'() { given: - File one = buildJar('one.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', + def one = buildJar('one.jar') + .insert('META-INF/services/org.apache.maven.Shade', 'one # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'one') + .insert('META-INF/services/com.acme.Foo', 'one') .write() - File two = buildJar('two.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', + def two = buildJar('two.jar') + .insert('META-INF/services/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'two') + .insert('META-INF/services/com.acme.Foo', 'two') .write() buildFile << """ @@ -61,10 +60,10 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'service resource transformer alternate path'() { given: - File one = buildJar('one.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + def one = buildJar('one.jar').insert('META-INF/foo/org.apache.maven.Shade', 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + def two = buildJar('two.jar').insert('META-INF/foo/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file').write() buildFile << """ @@ -96,16 +95,16 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'service resource transformer short syntax'() { given: - File one = buildJar('one.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', + def one = buildJar('one.jar') + .insert('META-INF/services/org.apache.maven.Shade', 'one # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'one') + .insert('META-INF/services/com.acme.Foo', 'one') .write() - File two = buildJar('two.jar') - .insertFile('META-INF/services/org.apache.maven.Shade', + def two = buildJar('two.jar') + .insert('META-INF/services/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file') - .insertFile('META-INF/services/com.acme.Foo', 'two') + .insert('META-INF/services/com.acme.Foo', 'two') .write() buildFile << """ @@ -141,23 +140,23 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'service resource transformer short syntax relocation'() { given: - File one = buildJar('one.jar') - .insertFile('META-INF/services/java.sql.Driver', + def one = buildJar('one.jar') + .insert('META-INF/services/java.sql.Driver', '''oracle.jdbc.OracleDriver org.apache.hive.jdbc.HiveDriver'''.stripIndent()) - .insertFile('META-INF/services/org.apache.axis.components.compiler.Compiler', + .insert('META-INF/services/org.apache.axis.components.compiler.Compiler', 'org.apache.axis.components.compiler.Javac') - .insertFile('META-INF/services/org.apache.commons.logging.LogFactory', + .insert('META-INF/services/org.apache.commons.logging.LogFactory', 'org.apache.commons.logging.impl.LogFactoryImpl') .write() - File two = buildJar('two.jar') - .insertFile('META-INF/services/java.sql.Driver', + def two = buildJar('two.jar') + .insert('META-INF/services/java.sql.Driver', '''org.apache.derby.jdbc.AutoloadedDriver com.mysql.jdbc.Driver'''.stripIndent()) - .insertFile('META-INF/services/org.apache.axis.components.compiler.Compiler', + .insert('META-INF/services/org.apache.axis.components.compiler.Compiler', 'org.apache.axis.components.compiler.Jikes') - .insertFile('META-INF/services/org.apache.commons.logging.LogFactory', + .insert('META-INF/services/org.apache.commons.logging.LogFactory', 'org.mortbay.log.Factory') .write() @@ -207,10 +206,10 @@ org.mortbay.log.Factory'''.stripIndent() def 'service resource transformer short syntax alternate path'() { given: - File one = buildJar('one.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + def one = buildJar('one.jar').insert('META-INF/foo/org.apache.maven.Shade', 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('META-INF/foo/org.apache.maven.Shade', + def two = buildJar('two.jar').insert('META-INF/foo/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file').write() buildFile << """ @@ -240,7 +239,7 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() @Issue(['SHADOW-70', 'SHADOW-71']) def 'apply transformers to project resources'() { given: - File one = buildJar('one.jar').insertFile('META-INF/services/shadow.Shadow', + def one = buildJar('one.jar').insert('META-INF/services/shadow.Shadow', 'one # NOTE: No newline terminates this line/file').write() repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', @@ -277,10 +276,10 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def 'appending transformer'() { given: - File one = buildJar('one.jar').insertFile('test.properties', + def one = buildJar('one.jar').insert('test.properties', 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('test.properties', + def two = buildJar('two.jar').insert('test.properties', 'two # NOTE: No newline terminates this line/file').write() buildFile << """ @@ -313,10 +312,10 @@ two # NOTE: No newline terminates this line/file def 'appending transformer short syntax'() { given: - File one = buildJar('one.jar').insertFile('test.properties', + def one = buildJar('one.jar').insert('test.properties', 'one # NOTE: No newline terminates this line/file').write() - File two = buildJar('two.jar').insertFile('test.properties', + def two = buildJar('two.jar').insert('test.properties', 'two # NOTE: No newline terminates this line/file').write() buildFile << """ @@ -428,7 +427,7 @@ two # NOTE: No newline terminates this line/file def 'append xml files'() { given: - File xml1 = buildJar('xml1.jar').insertFile('properties.xml', + def xml1 = buildJar('xml1.jar').insert('properties.xml', ''' @@ -437,7 +436,7 @@ two # NOTE: No newline terminates this line/file '''.stripIndent() ).write() - File xml2 = buildJar('xml2.jar').insertFile('properties.xml', + def xml2 = buildJar('xml2.jar').insert('properties.xml', ''' @@ -602,14 +601,14 @@ two # NOTE: No newline terminates this line/file def 'Groovy extension module transformer'() { given: def one = buildJar('one.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=foo moduleVersion=1.0.5 extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() def two = buildJar('two.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=bar moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension @@ -646,14 +645,14 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( def 'Groovy extension module transformer works for Groovy2_5+'() { given: def one = buildJar('one.jar') - .insertFile('META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=foo moduleVersion=1.0.5 extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() def two = buildJar('two.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=bar moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension @@ -691,14 +690,14 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( def 'Groovy extension module transformer short syntax'() { given: def one = buildJar('one.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=foo moduleVersion=1.0.5 extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() def two = buildJar('two.jar') - .insertFile('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', + .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', '''moduleName=bar moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension @@ -766,8 +765,4 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( 'ServiceFileTransformer' | '' 'XmlAppendingTransformer' | '' } - - private String escapedPath(File file) { - file.path.replaceAll('\\\\', '\\\\\\\\') - } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy index 72ebb78d4..15a08ced3 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy @@ -1,6 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.caching -import com.github.jengelman.gradle.plugins.shadow.util.PluginSpecification +import com.github.jengelman.gradle.plugins.shadow.BasePluginSpecification import org.apache.commons.io.FileUtils import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome @@ -11,7 +11,7 @@ import java.nio.file.Path import static org.gradle.testkit.runner.TaskOutcome.FROM_CACHE import static org.gradle.testkit.runner.TaskOutcome.SUCCESS -abstract class AbstractCachingSpec extends PluginSpecification { +abstract class AbstractCachingSpec extends BasePluginSpecification { @TempDir Path alternateDir @@ -46,10 +46,6 @@ abstract class AbstractCachingSpec extends PluginSpecification { return runner.withProjectDir(alternateDir.toFile()).withArguments(cacheArguments).build() } - private String escapedPath(File file) { - file.path.replaceAll('\\\\', '\\\\\\\\') - } - void assertShadowJarHasResult(TaskOutcome expectedOutcome) { def result = runWithCacheEnabled(shadowJarTask) assert result.task(shadowJarTask).outcome == expectedOutcome diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy deleted file mode 100644 index 2e9022905..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.groovy +++ /dev/null @@ -1,60 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileModule -import groovy.transform.InheritConstructors -import org.apache.commons.io.IOUtils - -@InheritConstructors -class AppendableMavenFileModule extends MavenFileModule { - - Map> contents = [:].withDefault { [:] } as Map> - Map files = [:] - - AppendableMavenFileModule use(File file) { - return use('', file) - } - - AppendableMavenFileModule use(String classifier, File file) { - files[classifier] = file - return this - } - - AppendableMavenFileModule insertFile(String path, String content) { - insertFile('', path, content) - return this - } - - AppendableMavenFileModule insertFile(String classifier, String path, String content) { - contents[classifier][path] = content - return this - } - - @Override - File publishArtifact(Map artifact) { - def artifactFile = artifactFile(artifact) - if (type == 'pom') { - return artifactFile - } - String classifier = (String) artifact['classifier'] ?: '' - if (files.containsKey(classifier)) { - publishWithStream(artifactFile) { OutputStream os -> - IOUtils.copy(files[classifier].newInputStream(), os) - } - } else { - publishWithStream(artifactFile) { OutputStream os -> - writeJar(os, contents[classifier]) - } - } - return artifactFile - } - - static void writeJar(OutputStream os, Map contents) { - if (contents) { - JarBuilder builder = new JarBuilder(os) - contents.each { path, content -> - builder.withFile(path, content) - } - builder.build() - } - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy deleted file mode 100644 index 18e4331da..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.groovy +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileRepository -import groovy.transform.InheritConstructors - -@InheritConstructors -class AppendableMavenFileRepository extends MavenFileRepository { - - @Override - AppendableMavenFileModule module(String groupId, String artifactId, String version = '1.0') { - def artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") - return new AppendableMavenFileModule(artifactDir, groupId, artifactId, version as String) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy deleted file mode 100644 index 936c0bbf9..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/FileExtensions.groovy +++ /dev/null @@ -1,26 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -/** - * TODO: this is used as extensions for Groovy, could be replaced after migrated to Kotlin. - * Registered in resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule. - */ -final class FileExtensions { - static final File resolve(File file, String relativePath) { - try { - return new File(file, relativePath) - } catch (RuntimeException e) { - throw new RuntimeException(String.format("Could not locate file '%s' relative to '%s'.", Arrays.toString(relativePath), file), e) - } - } - - static final File createDir(File file) { - if (file.mkdirs()) { - return file - } - if (file.isDirectory()) { - return file - } - throw new AssertionError("Problems creating dir: " + file - + ". Diagnostics: exists=" + file.exists() + ", isFile=" + file.isFile() + ", isDirectory=" + file.isDirectory()) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy deleted file mode 100644 index 90dddd2f6..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.groovy +++ /dev/null @@ -1,218 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import com.github.jengelman.gradle.plugins.shadow.util.repo.AbstractModule -import groovy.xml.MarkupBuilder -import groovy.xml.XmlParser - -import java.text.SimpleDateFormat - -abstract class AbstractMavenModule extends AbstractModule implements MavenModule { - protected static final String MAVEN_METADATA_FILE = "maven-metadata.xml" - final File moduleDir - final String groupId - final String artifactId - final String version - String parentPomSection - String type = 'jar' - String packaging - int publishCount = 1 - private final List dependencies = [] - private final List artifacts = [] - final updateFormat = new SimpleDateFormat("yyyyMMddHHmmss") - final timestampFormat = new SimpleDateFormat("yyyyMMdd.HHmmss") - - AbstractMavenModule(File moduleDir, String groupId, String artifactId, String version) { - this.moduleDir = moduleDir - this.groupId = groupId - this.artifactId = artifactId - this.version = version - } - - abstract boolean getUniqueSnapshots() - - String getPublishArtifactVersion() { - if (uniqueSnapshots && version.endsWith("-SNAPSHOT")) { - return "${version.replaceFirst('-SNAPSHOT$', '')}-${getUniqueSnapshotVersion()}" - } - return version - } - - private String getUniqueSnapshotVersion() { - assert uniqueSnapshots && version.endsWith('-SNAPSHOT') - if (metaDataFile.isFile()) { - def metaData = new XmlParser().parse(metaDataFile) - def timestamp = metaData.versioning.snapshot.timestamp[0].text().trim() - def build = metaData.versioning.snapshot.buildNumber[0].text().trim() - return "${timestamp}-${build}" - } - return "${timestampFormat.format(publishTimestamp)}-${publishCount}" - } - - MavenModule dependsOn(String... dependencyArtifactIds) { - for (String id : dependencyArtifactIds) { - dependsOn(groupId, id, '1.0') - } - return this - } - - @Override - MavenModule dependsOn(String groupId, String artifactId, String version) { - this.dependencies << [groupId: groupId, artifactId: artifactId, version: version, type: type] - return this - } - - String getPackaging() { - return packaging - } - - List getDependencies() { - return dependencies - } - - @Override - File getPomFile() { - return moduleDir.resolve("$artifactId-${publishArtifactVersion}.pom") - } - - @Override - File getMetaDataFile() { - moduleDir.resolve(MAVEN_METADATA_FILE) - } - - File getRootMetaDataFile() { - moduleDir.parentFile.resolve(MAVEN_METADATA_FILE) - } - - File artifactFile(Map options) { - def artifact = toArtifact(options) - def fileName = "$artifactId-${publishArtifactVersion}.${artifact.type}" - if (artifact.classifier) { - fileName = "$artifactId-$publishArtifactVersion-${artifact.classifier}.${artifact.type}" - } - return moduleDir.resolve(fileName) - } - - protected Map toArtifact(Map options) { - options = new HashMap(options) - def artifact = [type: options.remove('type') ?: type, classifier: options.remove('classifier') ?: null] - assert options.isEmpty(): "Unknown options : ${options.keySet()}" - return artifact - } - - Date getPublishTimestamp() { - return new Date(updateFormat.parse("20100101120000").time + publishCount * 1000) - } - - @Override - MavenModule publishPom() { - moduleDir.createDir() - def rootMavenMetaData = getRootMetaDataFile() - - updateRootMavenMetaData(rootMavenMetaData) - - if (publishesMetaDataFile()) { - publishWithWriter(metaDataFile) { Writer writer -> - writer << getMetaDataFileContent() - } - } - - publishWithWriter(pomFile) { Writer writer -> - def pomPackaging = packaging ?: type - writer << """ - - - 4.0.0 - $groupId - $artifactId - $pomPackaging - $version - Published on $publishTimestamp - """.stripIndent() - - if (parentPomSection) { - writer << "\n$parentPomSection\n" - } - - if (!dependencies.empty) { - writer << "" - } - - dependencies.each { dependency -> - def typeAttribute = dependency['type'] == null ? "" : "$dependency.type" - writer << """ - - $dependency.groupId - $dependency.artifactId - $dependency.version - $typeAttribute - """.stripIndent() - } - - if (!dependencies.empty) { - writer << "" - } - - writer << "\n" - } - return this - } - - private void updateRootMavenMetaData(File rootMavenMetaData) { - def allVersions = rootMavenMetaData.exists() ? new XmlParser().parseText(rootMavenMetaData.text).versioning.versions.version*.value().flatten() : [] - allVersions << version - publishWithWriter(rootMavenMetaData) { Writer writer -> - def builder = new MarkupBuilder(writer) - builder.metadata { - groupId(groupId) - artifactId(artifactId) - version(allVersions.max()) - versioning { - if (uniqueSnapshots && version.endsWith("-SNAPSHOT")) { - snapshot { - timestamp(timestampFormat.format(publishTimestamp)) - buildNumber(publishCount) - lastUpdated(updateFormat.format(publishTimestamp)) - } - } else { - versions { - allVersions.each { currVersion -> - version(currVersion) - } - } - } - } - } - } - } - - abstract String getMetaDataFileContent() - - @Override - MavenModule publish() { - - publishPom() - artifacts.each { artifact -> - publishArtifact(artifact as Map) - } - publishArtifact([:]) - return this - } - - File publishArtifact(Map artifact) { - def artifactFile = artifactFile(artifact) - if (type == 'pom') { - return artifactFile - } - publishWithWriter(artifactFile) { Writer writer -> - writer << "${artifactFile.name} : $artifactContent" - } - return artifactFile - } - - protected String getArtifactContent() { - // Some content to include in each artifact, so that its size and content varies on each publish - return (0..publishCount).join("-") - } - - protected abstract boolean publishesMetaDataFile() -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy deleted file mode 100644 index 80d482224..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.groovy +++ /dev/null @@ -1,46 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import org.jetbrains.annotations.NotNull - -class MavenFileModule extends AbstractMavenModule { - private boolean uniqueSnapshots = true - - MavenFileModule(File moduleDir, String groupId, String artifactId, String version) { - super(moduleDir, groupId, artifactId, version) - } - - @Override - boolean getUniqueSnapshots() { - return uniqueSnapshots - } - - @Override - String getMetaDataFileContent() { - """ - - - $groupId - $artifactId - $version - - - ${timestampFormat.format(publishTimestamp)} - $publishCount - - ${updateFormat.format(publishTimestamp)} - - - """.stripIndent() - } - - @Override - protected void onPublish(@NotNull File file) { - sha1File(file) - md5File(file) - } - - @Override - protected boolean publishesMetaDataFile() { - uniqueSnapshots && version.endsWith("-SNAPSHOT") - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy deleted file mode 100644 index fdff3a8ed..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.groovy +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -/** - * A fixture for dealing with file Maven repositories. - */ -class MavenFileRepository implements MavenRepository { - final File rootDir - - MavenFileRepository(File rootDir) { - this.rootDir = rootDir - } - - @Override - URI getUri() { - return rootDir.toURI() - } - - @Override - MavenFileModule module(String groupId, String artifactId, String version = '1.0') { - def artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") - return new MavenFileModule(artifactDir, groupId, artifactId, version as String) - } -} diff --git a/src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule b/src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule deleted file mode 100644 index bb7d5044f..000000000 --- a/src/funcTest/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule +++ /dev/null @@ -1,3 +0,0 @@ -moduleName = groovy-extensions -moduleVersion = ${moduleVersion} -extensionClasses =com.github.jengelman.gradle.plugins.shadow.util.FileExtensions diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt index 798a06bf7..41b102d82 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt @@ -1,20 +1,28 @@ package com.github.jengelman.gradle.plugins.shadow.util -import java.io.File +import java.io.OutputStream +import java.nio.file.Path +import kotlin.io.path.outputStream -class AppendableJar(private val file: File) { +class AppendableJar(private val outputPath: Path) { private val contents = mutableMapOf() - fun insertFile(path: String, content: String): AppendableJar = apply { + fun insert(path: String, content: String): AppendableJar = apply { contents[path] = content } - fun write(): File { - val builder = JarBuilder(file.outputStream()) - contents.forEach { (path, content) -> - builder.withFile(path, content) + fun write(): Path { + write(contents, outputPath.outputStream()) + return outputPath + } + + companion object { + fun write(contents: Map, outputStream: OutputStream) { + val builder = JarBuilder(outputStream) + contents.forEach { (path, content) -> + builder.withPath(path, content) + } + builder.build() } - builder.build() - return file } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt new file mode 100644 index 000000000..d41213392 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt @@ -0,0 +1,46 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileModule +import java.nio.file.Path +import kotlin.io.path.inputStream + +class AppendableMavenFileModule(module: MavenFileModule) : MavenFileModule(module.moduleDir, module.groupId, module.artifactId, module.version) { + + private val contents = mutableMapOf>().withDefault { mutableMapOf() } + private val paths = mutableMapOf() + + fun use(path: Path): AppendableMavenFileModule { + return use("", path) + } + + fun use(classifier: String, path: Path): AppendableMavenFileModule = apply { + paths[classifier] = path + } + + fun insertFile(path: String, content: String): AppendableMavenFileModule { + return insertFile("", path, content) + } + + fun insertFile(classifier: String, path: String, content: String): AppendableMavenFileModule = apply { + contents.getOrPut(classifier) { mutableMapOf() }[path] = content + } + + override fun publishArtifact(artifact: Map): Path { + val artifactPath = artifactPath(artifact) + if (type == "pom") { + return artifactPath + } + val classifier = artifact["classifier"] as? String ?: "" + val classifierPath = paths[classifier] + if (classifierPath != null) { + publish(artifactPath) { os -> + classifierPath.inputStream().copyTo(os) + } + } else { + publish(artifactPath) { os -> + AppendableJar.write(contents[classifier].orEmpty(), os) + } + } + return artifactPath + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt new file mode 100644 index 000000000..e2ce40630 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt @@ -0,0 +1,11 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileRepository +import java.nio.file.Path + +class AppendableMavenFileRepository(rootDir: Path) : MavenFileRepository(rootDir) { + + override fun module(groupId: String, artifactId: String, version: String): AppendableMavenFileModule { + return AppendableMavenFileModule(super.module(groupId, artifactId, version)) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt deleted file mode 100644 index 492a8a526..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashUtil.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util -import java.io.File -import java.io.FileInputStream -import java.io.FileNotFoundException -import java.io.IOException -import java.io.InputStream -import java.io.UncheckedIOException -import java.security.MessageDigest -import java.security.NoSuchAlgorithmException -import org.gradle.internal.UncheckedException - -object HashUtil { - fun createHash(file: File, algorithm: String): HashValue { - try { - return createHash(FileInputStream(file), algorithm) - } catch (e: UncheckedIOException) { - // Catch any unchecked io exceptions and add the file path for troubleshooting - throw UncheckedIOException( - "Failed to create $algorithm hash for file ${file.absolutePath}.", - e.cause, - ) - } catch (e: FileNotFoundException) { - throw UncheckedIOException(e) - } - } - - private fun createHash(inputStream: InputStream, algorithm: String): HashValue { - val messageDigest: MessageDigest - try { - messageDigest = createMessageDigest(algorithm) - val buffer = ByteArray(4096) - inputStream.use { - while (true) { - val nread = it.read(buffer) - if (nread < 0) { - break - } - messageDigest.update(buffer, 0, nread) - } - } - } catch (e: IOException) { - throw UncheckedIOException(e) - } - return HashValue(messageDigest.digest()) - } - - private fun createMessageDigest(algorithm: String): MessageDigest { - try { - return MessageDigest.getInstance(algorithm) - } catch (e: NoSuchAlgorithmException) { - throw UncheckedException.throwAsUncheckedException(e) - } - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt deleted file mode 100644 index 821a0953b..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/HashValue.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import java.math.BigInteger - -data class HashValue(val digest: BigInteger) { - constructor(digest: ByteArray) : this(BigInteger(1, digest)) -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt index d4c81cabb..2372d5823 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt @@ -5,33 +5,28 @@ import java.util.jar.JarEntry import java.util.jar.JarOutputStream class JarBuilder(os: OutputStream) { - private val entries = mutableListOf() + private val entries = mutableSetOf() private val jos = JarOutputStream(os) private fun addDirectory(name: String) { - if (!entries.contains(name)) { + if (entries.add(name)) { val parent = name.substringBeforeLast('/', "") if (parent.isNotEmpty() && !entries.contains(parent)) { addDirectory(parent) } - // directory entries must end in "/" - val entry = JarEntry("$name/") - jos.putNextEntry(entry) - entries.add(name) + jos.putNextEntry(JarEntry("$name/")) } } - fun withFile(path: String, data: String): JarBuilder { + fun withPath(path: String, data: String): JarBuilder { val idx = path.lastIndexOf('/') if (idx != -1) { addDirectory(path.substring(0, idx)) } - if (!entries.contains(path)) { - val entry = JarEntry(path) - jos.putNextEntry(entry) - entries.add(path) - data.byteInputStream().use { it.copyTo(jos) } + if (entries.add(path)) { + jos.putNextEntry(JarEntry(path)) + data.byteInputStream().copyTo(jos) } return this } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt index 51d740e5d..827964aa4 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt @@ -1,63 +1,79 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo -import com.github.jengelman.gradle.plugins.shadow.util.HashUtil -import java.io.File import java.io.OutputStream -import java.io.Writer +import java.io.UncheckedIOException import java.math.BigInteger +import java.nio.file.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.exists +import kotlin.io.path.moveTo +import kotlin.io.path.name +import kotlin.io.path.outputStream +import kotlin.io.path.readBytes +import kotlin.io.path.writeText +import okio.ByteString.Companion.toByteString abstract class AbstractModule { - protected abstract fun onPublish(file: File) + protected open fun postPublish(path: Path) = Unit - protected fun publishWithWriter(file: File, action: (Writer) -> Unit) { - publishCommon(file) { it.writer().use(action) } - } - - protected fun publishWithStream(file: File, action: (OutputStream) -> Unit) { - publishCommon(file) { it.outputStream().use(action) } - } + protected fun publish(path: Path, action: (OutputStream) -> Unit) { + val hashBefore = if (path.exists()) getHash(path, "sha1") else null + val tempPath = path.resolveSibling("${path.name}.tmp") + tempPath.outputStream().use(action) - private fun publishCommon(file: File, action: (File) -> Unit) { - val hashBefore = if (file.exists()) getHash(file, "sha1") else null - val tempFile = file.resolveSibling("${file.name}.tmp") - action(tempFile) - - val hashAfter = getHash(tempFile, "sha1") + val hashAfter = getHash(tempPath, "sha1") if (hashAfter == hashBefore) { // Already published return } - check(!file.exists() || file.delete()) - check(tempFile.renameTo(file)) - onPublish(file) + tempPath.moveTo(path, true) + check(path.exists()) + writeSha1Path(path) + writeMd5Path(path) + + postPublish(path) } companion object { - @JvmStatic - fun sha1File(file: File): File { - return hashFile(file, "sha1", 40) + fun writeSha1Path(path: Path): Path { + return writeHashPath(path, "sha1", 40) } - @JvmStatic - fun md5File(file: File): File { - return hashFile(file, "md5", 32) + fun writeMd5Path(path: Path): Path { + return writeHashPath(path, "md5", 32) } - private fun hashFile(file: File, algorithm: String, len: Int): File { - val hashFile = getHashFile(file, algorithm) - val hash = getHash(file, algorithm) - hashFile.writeText("$hash${len}x") - return hashFile + private fun writeHashPath(path: Path, algorithm: String, len: Int): Path { + val hashPath = getHashPath(path, algorithm) + val hash = getHash(path, algorithm) + hashPath.writeText("$hash${len}x") + return hashPath } - private fun getHashFile(file: File, algorithm: String): File { - return File(file.parentFile, "${file.name}.$algorithm") + private fun getHashPath(path: Path, algorithm: String): Path { + return path.resolveSibling("${path.name}.$algorithm") } - private fun getHash(file: File, algorithm: String): BigInteger { - return HashUtil.createHash(file, algorithm.uppercase()).digest + private fun getHash(path: Path, algorithm: String): BigInteger { + try { + val byteString = path.readBytes().toByteString() + val byteArray = when (algorithm.uppercase()) { + "MD5" -> byteString.md5() + "SHA1" -> byteString.sha1() + "SHA256" -> byteString.sha256() + "SHA512" -> byteString.sha512() + else -> throw IllegalArgumentException("Unsupported algorithm: $algorithm") + }.toByteArray() + return BigInteger(1, byteArray) + } catch (e: UncheckedIOException) { + // Catch any unchecked io exceptions and add the file path for troubleshooting + throw UncheckedIOException( + "Failed to create $algorithm hash for file ${path.absolutePathString()}.", + e.cause, + ) + } } } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt new file mode 100644 index 000000000..8d4ada5d6 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt @@ -0,0 +1,184 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import com.github.jengelman.gradle.plugins.shadow.util.repo.AbstractModule +import java.nio.file.Path +import java.text.SimpleDateFormat +import java.util.Date +import kotlin.io.path.createDirectories +import kotlin.io.path.exists +import kotlin.io.path.isRegularFile +import kotlin.io.path.name +import kotlin.io.path.reader +import org.apache.maven.artifact.repository.metadata.Metadata +import org.apache.maven.artifact.repository.metadata.Snapshot +import org.apache.maven.artifact.repository.metadata.Versioning +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader +import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer +import org.apache.maven.model.Dependency +import org.apache.maven.model.Model +import org.apache.maven.model.io.xpp3.MavenXpp3Writer + +abstract class AbstractMavenModule( + val moduleDir: Path, + val groupId: String, + val artifactId: String, + val version: String, +) : AbstractModule(), + MavenModule { + + protected val updateFormat = SimpleDateFormat("yyyyMMddHHmmss") + protected val timestampFormat = SimpleDateFormat("yyyyMMdd.HHmmss") + protected val dependencies = mutableListOf() + protected val artifacts = mutableListOf>() + + protected var type: String = "jar" + protected var packaging: String? = null + protected var publishCount: Int = 1 + + protected abstract val isUniqueSnapshots: Boolean + protected abstract val isPublishesMetaDataFile: Boolean + + fun dependsOn(artifactId: String): MavenModule { + return dependsOn(groupId = groupId, artifactId = artifactId, version = version) + } + + override fun dependsOn(groupId: String, artifactId: String, version: String): MavenModule = apply { + val dep = Dependency().also { + it.groupId = groupId + it.artifactId = artifactId + it.version = version + } + dependencies.add(dep) + } + + override val pomPath: Path + get() = moduleDir.resolve("$artifactId-$publishArtifactVersion.pom") + + override val metaDataPath: Path + get() = moduleDir.resolve(MAVEN_METADATA_FILE) + + val rootMetaDataPath: Path + get() = moduleDir.resolveSibling(MAVEN_METADATA_FILE) + + fun artifactPath(options: Map): Path { + val artifact = toArtifact(options) + var fileName = "$artifactId-$publishArtifactVersion.${artifact["type"]}" + if (artifact["classifier"] != null) { + fileName = "$artifactId-$publishArtifactVersion-${artifact["classifier"]}.${artifact["type"]}" + } + return moduleDir.resolve(fileName) + } + + override fun publishPom(): MavenModule = apply { + moduleDir.createDirectories() + val rootMavenMetaData = rootMetaDataPath + updateRootMavenMetaData(rootMavenMetaData) + + if (isPublishesMetaDataFile) { + publish(metaDataPath) { outputStream -> + MetadataXpp3Writer().write(outputStream, getMetaData(emptyList())) + } + } + + publish(pomPath) { outputStream -> + val pomPackaging = packaging ?: type + val model = Model().also { + it.modelVersion = "4.0.0" + it.groupId = groupId + it.artifactId = artifactId + it.version = version + it.packaging = pomPackaging + it.description = "Published on $publishTimestamp" + it.dependencies = dependencies + } + MavenXpp3Writer().write(outputStream, model) + } + } + + open fun getMetaData(versions: List): Metadata = Metadata().also { + it.groupId = groupId + it.artifactId = artifactId + it.version = version + it.versioning = Versioning().also { versioning -> + versioning.versions = versions + versioning.lastUpdated = updateFormat.format(publishTimestamp) + if (isUniqueSnapshots && version.endsWith("-SNAPSHOT")) { + versioning.snapshot = Snapshot().apply { + timestamp = timestampFormat.format(publishTimestamp) + buildNumber = publishCount + } + } + } + } + + override fun publish(): MavenModule = apply { + publishPom() + artifacts.forEach { artifact -> + publishArtifact(artifact) + } + publishArtifact(emptyMap()) + } + + open fun publishArtifact(artifact: Map): Path { + val artifactPath = artifactPath(artifact) + if (type == "pom") { + return artifactPath + } + publish(artifactPath) { outputStream -> + outputStream.write("${artifactPath.name} : $artifactContent".toByteArray()) + } + return artifactPath + } + + protected fun toArtifact(options: Map): Map { + val artifact = mutableMapOf( + "type" to (options["type"] ?: type), + "classifier" to options["classifier"], + ) + require(options.keys.isEmpty()) { "Unknown options : ${options.keys}" } + return artifact + } + + protected val publishArtifactVersion: String + get() = if (isUniqueSnapshots && version.endsWith("-SNAPSHOT")) { + "${version.removeSuffix("-SNAPSHOT")}-$uniqueSnapshotVersion" + } else { + version + } + + protected val publishTimestamp: Date + get() = Date(updateFormat.parse("20100101120000").time + publishCount * 1000) + + private fun updateRootMavenMetaData(rootMavenMetaData: Path) { + val allVersions = if (rootMavenMetaData.exists()) { + MetadataXpp3Reader().read(rootMavenMetaData.reader()).versioning.versions + } else { + mutableListOf() + } + allVersions.add(version) + publish(rootMavenMetaData) { outputStream -> + MetadataXpp3Writer().write(outputStream, getMetaData(allVersions)) + } + } + + private val artifactContent: String + // Some content to include in each artifact, so that its size and content varies on each pu + get() = (0..publishCount).joinToString("-") + + private val uniqueSnapshotVersion: String + get() { + require(isUniqueSnapshots && version.endsWith("-SNAPSHOT")) + return if (metaDataPath.isRegularFile()) { + val metaData = MetadataXpp3Reader().read(metaDataPath.reader()) + val timestamp = metaData.versioning.snapshot.timestamp + val build = metaData.versioning.snapshot.buildNumber + "$timestamp-$build" + } else { + "${timestampFormat.format(publishTimestamp)}-$publishCount" + } + } + + protected companion object { + const val MAVEN_METADATA_FILE = "maven-metadata.xml" + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt new file mode 100644 index 000000000..0807d4021 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt @@ -0,0 +1,35 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import java.nio.file.Path +import org.apache.maven.artifact.repository.metadata.Metadata +import org.apache.maven.artifact.repository.metadata.Snapshot +import org.apache.maven.artifact.repository.metadata.Versioning + +open class MavenFileModule( + moduleDir: Path, + groupId: String, + artifactId: String, + version: String, +) : AbstractMavenModule(moduleDir, groupId, artifactId, version) { + + override val isUniqueSnapshots: Boolean = true + + override fun getMetaData(versions: List): Metadata { + return Metadata().also { + it.groupId = groupId + it.artifactId = artifactId + it.version = version + it.versioning = Versioning().also { versioning -> + versioning.versions = versions + versioning.snapshot = Snapshot().apply { + timestamp = timestampFormat.format(publishTimestamp) + buildNumber = publishCount + } + versioning.lastUpdated = updateFormat.format(publishTimestamp) + } + } + } + + override val isPublishesMetaDataFile: Boolean + get() = isUniqueSnapshots && version.endsWith("-SNAPSHOT") +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt new file mode 100644 index 000000000..e1ba0c089 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt @@ -0,0 +1,17 @@ +package com.github.jengelman.gradle.plugins.shadow.util.repo.maven + +import java.net.URI +import java.nio.file.Path + +/** + * A fixture for dealing with file Maven repositories. + */ +open class MavenFileRepository(val rootDir: Path) : MavenRepository { + + override val uri: URI = rootDir.toUri() + + override fun module(groupId: String, artifactId: String, version: String): MavenFileModule { + val artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") + return MavenFileModule(artifactDir, groupId, artifactId, version) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt index b77d25886..e484f2a96 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt @@ -1,6 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.util.repo.maven -import java.io.File +import java.nio.file.Path interface MavenModule { /** @@ -16,7 +16,7 @@ interface MavenModule { fun dependsOn(groupId: String, artifactId: String, version: String): MavenModule - val pomFile: File + val pomPath: Path - val metaDataFile: File + val metaDataPath: Path } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt index a128e07f5..95ca83c86 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt @@ -8,7 +8,5 @@ import java.net.URI interface MavenRepository { val uri: URI - fun module(groupId: String, artifactId: String): MavenModule - fun module(groupId: String, artifactId: String, version: String): MavenModule } From 696b1a7eafa2f74647412e5388542faeb5e88475 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 25 Dec 2024 22:57:48 -0500 Subject: [PATCH 114/941] Polish ignored functional tests (#1120) * Remove deprecated `doesn't error when adding aspectj plugin` * Enable `does not error on relocating java9 classes` * Enable `dependency exclusions affect UP-TO-DATE check` * Tweak IgnoreIf for `Compatible with Gradle #version` * Fix `project exclusions affect UP-TO-DATE check` --- .../plugins/shadow/FilteringSpec.groovy | 14 ++-- .../plugins/shadow/RelocationSpec.groovy | 5 +- .../plugins/shadow/ShadowPluginSpec.groovy | 69 ++----------------- 3 files changed, 12 insertions(+), 76 deletions(-) diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy index 60dc6ac47..903586ce5 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome -import spock.lang.Ignore import spock.lang.Issue class FilteringSpec extends BasePluginSpecification { @@ -124,7 +123,6 @@ class FilteringSpec extends BasePluginSpecification { } @Issue("SHADOW-54") - @Ignore("TODO - need to figure out the test pollution here") def "dependency exclusions affect UP-TO-DATE check"() { given: repo.module('shadow', 'c', '1.0') @@ -173,7 +171,6 @@ class FilteringSpec extends BasePluginSpecification { } @Issue("SHADOW-62") - @Ignore def "project exclusions affect UP-TO-DATE check"() { given: repo.module('shadow', 'c', '1.0') @@ -206,11 +203,8 @@ class FilteringSpec extends BasePluginSpecification { doesNotContain(output, ['d.properties']) when: 'Update build file shadowJar dependency exclusion' - buildFile.text << ''' - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - exclude 'a.properties' - } - '''.stripIndent() + buildFile.text = buildFile.text.replace('exclude(dependency(\'shadow:d:1.0\'))', + 'exclude \'a.properties\'') BuildResult result = run('shadowJar') @@ -218,10 +212,10 @@ class FilteringSpec extends BasePluginSpecification { assert result.task(':shadowJar').outcome == TaskOutcome.SUCCESS and: - contains(output, ['a2.properties', 'b.properties', 'd.properties']) + contains(output, ['a2.properties', 'b.properties', 'c.properties', 'd.properties']) and: - doesNotContain(output, ['a.properties', 'c.properties']) + doesNotContain(output, ['a.properties']) } def "include dependency, excluding all others"() { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy index 244fbbcdf..0b7d9d004 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow -import spock.lang.Ignore import spock.lang.Issue import java.util.jar.Attributes @@ -288,7 +287,6 @@ class RelocationSpec extends BasePluginSpecification { } @Issue("SHADOW-294") - @Ignore def "does not error on relocating java9 classes"() { given: buildFile << """ @@ -308,7 +306,7 @@ class RelocationSpec extends BasePluginSpecification { } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - zip64 true + zip64 = true relocate 'com.google.protobuf', 'shaded.com.google.protobuf' relocate 'io.netty', 'shaded.io.netty' } @@ -319,6 +317,5 @@ class RelocationSpec extends BasePluginSpecification { then: noExceptionThrown() - } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index 33d49dcc9..623056ca8 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -55,10 +55,13 @@ class ShadowPluginSpec extends BasePluginSpecification { } - @IgnoreIf({ - // Gradle 8.3 doesn't support Java 21. - JavaVersion.current().majorVersion.toInteger() >= 21 - }) + @IgnoreIf( + reason = "Gradle 8.3 doesn't support Java 21.", + value = { + // Gradle 8.3 doesn't support Java 21. + JavaVersion.current().majorVersion.toInteger() >= 21 + } + ) @Unroll def 'Compatible with Gradle #version'() { given: @@ -1114,64 +1117,6 @@ class ShadowPluginSpec extends BasePluginSpecification { } - @Issue("SHADOW-303") - @Ignore("Plugin has been deprecated") - def "doesn't error when adding aspectj plugin"() { - given: - buildFile.text = """ - buildscript { - repositories { - maven { - url = "https://maven.eveoh.nl/content/repositories/releases" - } - } - - dependencies { - classpath "nl.eveoh:gradle-aspectj:2.0" - } - } - """.stripIndent() - - buildFile << defaultBuildScript - - buildFile << """ - project.ext { - aspectjVersion = '1.8.12' - } - - apply plugin: 'aspectj' - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - - repositories { - mavenCentral() - } - - runShadow { - args 'foo' - } - - """ - - file('src/main/java/myapp/Main.java') << """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.stripIndent() - - when: - BuildResult result = run('runShadow') - - then: 'tests that runShadow executed and exited' - assert result.output.contains('TestApp: Hello World! (foo)') - } - @Issue("https://github.com/GradleUp/shadow/issues/609") def "doesn't error when using application mainClass property"() { given: From 22e37556061b121cad377cf576c84e973adf8d1f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 26 Dec 2024 03:35:24 -0500 Subject: [PATCH 115/941] Simplify functional test build scripts (#1118) * Declare maven repos in settings.gradle * Remove unused integTest source set * Replace hardcodes and remove extra comments * Replace issue links * Remove all -s args, it's the default * Enable --warning-mode=fail * Merge ConfigureShadowRelocationSpec into RelocationSpec --- .../plugins/shadow/ApplicationSpec.groovy | 6 +- .../shadow/BasePluginSpecification.groovy | 45 ++++-- .../shadow/ConfigurationCacheSpec.groovy | 24 ++-- .../ConfigureShadowRelocationSpec.groovy | 40 ------ .../plugins/shadow/FilteringSpec.groovy | 72 ++++------ .../plugins/shadow/PublishingSpec.groovy | 7 +- .../plugins/shadow/RelocationSpec.groovy | 81 +++++++---- .../plugins/shadow/ShadowPluginSpec.groovy | 130 +++++++----------- .../plugins/shadow/TransformerSpec.groovy | 68 ++++----- .../shadow/caching/AbstractCachingSpec.groovy | 2 +- .../caching/MinimizationCachingSpec.groovy | 20 ++- .../caching/RelocationCachingSpec.groovy | 2 +- .../caching/ShadowJarCachingSpec.groovy | 14 +- .../caching/TransformCachingSpec.groovy | 16 +-- 14 files changed, 229 insertions(+), 298 deletions(-) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy index 77c888a38..939e3cbb6 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy @@ -120,7 +120,7 @@ class ApplicationSpec extends BasePluginSpecification { } rootProject.name = 'myapp' - """.stripIndent() + """.stripIndent() + getSettingsBuildScript(false) when: BuildResult result = run('runShadow') @@ -152,7 +152,7 @@ class ApplicationSpec extends BasePluginSpecification { jar?.close() } - @Issue('SHADOW-89') + @Issue('https://github.com/GradleUp/shadow/issues/89') def 'shadow application distributions should use shadow jar'() { given: repo.module('shadow', 'a', '1.0') @@ -204,7 +204,7 @@ class ApplicationSpec extends BasePluginSpecification { zipFile?.close() } - @Issue('SHADOW-90') + @Issue('https://github.com/GradleUp/shadow/issues/90') def 'installShadow does not execute dependent shadow task'() { given: repo.module('shadow', 'a', '1.0') diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy index f3393d6ec..6b377d860 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.AppendableJar import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository import org.codehaus.plexus.util.IOUtil @@ -25,35 +26,51 @@ abstract class BasePluginSpecification extends Specification { repo = repo() repo.module('junit', 'junit', '3.8.2').use(testJar).publish() - buildFile << defaultBuildScript - - settingsFile << ''' - rootProject.name = 'shadow' - ''' + buildFile << getDefaultBuildScript('java', true, true) + buildFile << System.lineSeparator() + settingsFile << settingsBuildScript + settingsFile << System.lineSeparator() } def cleanup() { println buildFile.text } - String getDefaultBuildScript(String javaPlugin = 'java') { + String getDefaultBuildScript( + String javaPlugin = 'java', + boolean withGroup = false, + boolean withVersion = false + ) { + def groupInfo = withGroup ? "group = 'shadow'" : "" + def versionInfo = withVersion ? "version = '1.0'" : "" + return """ plugins { id '${javaPlugin}' id 'com.gradleup.shadow' } - version = "1.0" - group = 'shadow' + $groupInfo + $versionInfo + """.stripIndent().trim() + } - sourceSets { - integTest - } + String getSettingsBuildScript(boolean withRootProject = true) { + def rootProjectInfo = withRootProject ? "rootProject.name = 'shadow'" : "" + return """ + dependencyResolutionManagement { + repositories { + maven { url = "${repo.uri}" } + mavenCentral() + } + } - repositories { maven { url = "${repo.uri}" } } - """.stripIndent() + $rootProjectInfo + """.stripIndent().trim() } + static def shadowJar = "tasks.named('shadowJar', ${ShadowJar.class.name})".trim() + GradleRunner getRunner() { GradleRunner.create() .withProjectDir(dir.toFile()) @@ -63,7 +80,7 @@ abstract class BasePluginSpecification extends Specification { } GradleRunner runner(Collection tasks) { - runner.withArguments(["-Dorg.gradle.warning.mode=all", "--configuration-cache", "--stacktrace"] + tasks.toList()) + runner.withArguments(["--warning-mode=fail", "--configuration-cache", "--stacktrace"] + tasks.toList()) } BuildResult run(String... tasks) { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy index 253db82bc..2391e809d 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy @@ -60,7 +60,7 @@ class ConfigurationCacheSpec extends BasePluginSpecification { def "configuration caching supports includes"() { given: buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { exclude 'a2.properties' } """.stripIndent() @@ -91,7 +91,7 @@ class ConfigurationCacheSpec extends BasePluginSpecification { """.stripIndent() file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -101,16 +101,14 @@ class ConfigurationCacheSpec extends BasePluginSpecification { public class Server {} """.stripIndent() file('server/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { minimize { exclude(dependency('junit:junit:.*')) } } - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -118,9 +116,9 @@ class ConfigurationCacheSpec extends BasePluginSpecification { def output = getFile('server/build/libs/server-all.jar') when: - run('shadowJar', '-s') + run('shadowJar') output.delete() - def result = run('shadowJar', '-s') + def result = run('shadowJar') then: output.exists() @@ -146,23 +144,21 @@ class ConfigurationCacheSpec extends BasePluginSpecification { public class Lib {} """.stripIndent() file('lib/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - repositories { maven { url = "${repo.uri}" } } dependencies { implementation "junit:junit:3.8.2" } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { configurations = [project.configurations.compileClasspath] } """.stripIndent() when: - run('shadowJar', '-s') - def result = run('shadowJar', '-s') + run('shadowJar') + def result = run('shadowJar') then: result.output.contains(":lib:shadowJar UP-TO-DATE") diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy deleted file mode 100644 index 654ef5800..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigureShadowRelocationSpec.groovy +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -class ConfigureShadowRelocationSpec extends BasePluginSpecification { - - def "auto relocate plugin dependencies"() { - given: - buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation = true - } - - dependencies { - implementation 'junit:junit:3.8.2' - } - """.stripIndent() - - when: - run('shadowJar', '-s') - - then: - contains(output, [ - 'META-INF/MANIFEST.MF', - 'shadow/junit/textui/ResultPrinter.class', - 'shadow/junit/textui/TestRunner.class', - 'shadow/junit/framework/Assert.class', - 'shadow/junit/framework/AssertionFailedError.class', - 'shadow/junit/framework/ComparisonCompactor.class', - 'shadow/junit/framework/ComparisonFailure.class', - 'shadow/junit/framework/Protectable.class', - 'shadow/junit/framework/Test.class', - 'shadow/junit/framework/TestCase.class', - 'shadow/junit/framework/TestFailure.class', - 'shadow/junit/framework/TestListener.class', - 'shadow/junit/framework/TestResult$1.class', - 'shadow/junit/framework/TestResult.class', - 'shadow/junit/framework/TestSuite$1.class', - 'shadow/junit/framework/TestSuite.class' - ]) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy index 903586ce5..b365e6da2 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy @@ -36,11 +36,9 @@ class FilteringSpec extends BasePluginSpecification { def 'exclude files'() { given: buildFile << """ - // tag::excludeFile[] - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { exclude 'a2.properties' } - // end::excludeFile[] """.stripIndent() when: @@ -63,19 +61,17 @@ class FilteringSpec extends BasePluginSpecification { .dependsOn('c') .publish() - buildFile << ''' - // tag::excludeDep[] + buildFile << """ dependencies { implementation 'shadow:d:1.0' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { dependencies { exclude(dependency('shadow:d:1.0')) } } - // end::excludeDep[] - '''.stripIndent() + """.stripIndent() when: run('shadowJar') @@ -87,7 +83,7 @@ class FilteringSpec extends BasePluginSpecification { doesNotContain(output, ['d.properties']) } - @Issue('SHADOW-83') + @Issue('https://github.com/GradleUp/shadow/issues/83') def "exclude dependency using wildcard syntax"() { given: repo.module('shadow', 'c', '1.0') @@ -98,19 +94,17 @@ class FilteringSpec extends BasePluginSpecification { .dependsOn('c') .publish() - buildFile << ''' - // tag::excludeDepWildcard[] + buildFile << """ dependencies { implementation 'shadow:d:1.0' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { dependencies { exclude(dependency('shadow:d:.*')) } } - // end::excludeDepWildcard[] - '''.stripIndent() + """.stripIndent() when: run('shadowJar') @@ -122,7 +116,7 @@ class FilteringSpec extends BasePluginSpecification { doesNotContain(output, ['d.properties']) } - @Issue("SHADOW-54") + @Issue("https://github.com/GradleUp/shadow/issues/54") def "dependency exclusions affect UP-TO-DATE check"() { given: repo.module('shadow', 'c', '1.0') @@ -133,17 +127,17 @@ class FilteringSpec extends BasePluginSpecification { .dependsOn('c') .publish() - buildFile << ''' + buildFile << """ dependencies { implementation 'shadow:d:1.0' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { dependencies { exclude(dependency('shadow:d:1.0')) } } - '''.stripIndent() + """.stripIndent() when: run('shadowJar') @@ -170,7 +164,7 @@ class FilteringSpec extends BasePluginSpecification { doesNotContain(output, ['c.properties']) } - @Issue("SHADOW-62") + @Issue("https://github.com/GradleUp/shadow/issues/62") def "project exclusions affect UP-TO-DATE check"() { given: repo.module('shadow', 'c', '1.0') @@ -181,17 +175,17 @@ class FilteringSpec extends BasePluginSpecification { .dependsOn('c') .publish() - buildFile << ''' + buildFile << """ dependencies { implementation 'shadow:d:1.0' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { dependencies { exclude(dependency('shadow:d:1.0')) } } - '''.stripIndent() + """.stripIndent() when: run('shadowJar') @@ -233,17 +227,17 @@ class FilteringSpec extends BasePluginSpecification { public class Passed {} '''.stripIndent() - buildFile << ''' + buildFile << """ dependencies { implementation 'shadow:d:1.0' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { dependencies { include(dependency('shadow:d:1.0')) } } - '''.stripIndent() + """.stripIndent() when: run('shadowJar') @@ -269,7 +263,7 @@ class FilteringSpec extends BasePluginSpecification { """.stripIndent() file('client/build.gradle') << """ - ${defaultBuildScript} + ${getDefaultBuildScript('java', false, true)} dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -280,19 +274,17 @@ class FilteringSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - ${defaultBuildScript} + ${getDefaultBuildScript('java', false, true)} - // tag::excludeProject[] dependencies { implementation project(':client') } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { dependencies { exclude(project(':client')) } } - // end::excludeProject[] """.stripIndent() File serverOutput = getFile('server/build/libs/server-1.0-all.jar') @@ -324,7 +316,7 @@ class FilteringSpec extends BasePluginSpecification { """.stripIndent() file('client/build.gradle') << """ - ${defaultBuildScript} + ${getDefaultBuildScript('java', false, true)} dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -335,18 +327,16 @@ class FilteringSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - ${defaultBuildScript} + ${getDefaultBuildScript('java', false, true)} dependencies { implementation project(':client') } - // tag::excludeSpec[] - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { dependencies { exclude(dependency { it.moduleGroup == 'junit' }) } } - // end::excludeSpec[] """.stripIndent() File serverOutput = getFile('server/build/libs/server-1.0-all.jar') @@ -370,13 +360,11 @@ class FilteringSpec extends BasePluginSpecification { def 'verify exclude precedence over include'() { given: buildFile << """ - // tag::excludeOverInclude[] - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { include '*.jar' include '*.properties' exclude 'a2.properties' } - // end::excludeOverInclude[] """.stripIndent() when: @@ -389,7 +377,7 @@ class FilteringSpec extends BasePluginSpecification { doesNotContain(output, ['a2.properties']) } - @Issue("SHADOW-69") + @Issue("https://github.com/GradleUp/shadow/issues/69") def "handle exclude with circular dependency"() { given: repo.module('shadow', 'c', '1.0') @@ -401,17 +389,17 @@ class FilteringSpec extends BasePluginSpecification { .dependsOn('c') .publish() - buildFile << ''' + buildFile << """ dependencies { implementation 'shadow:d:1.0' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { dependencies { exclude(dependency('shadow:d:1.0')) } } - '''.stripIndent() + """.stripIndent() when: run('shadowJar') diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy index 6f2616897..9b95a42f0 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy @@ -35,7 +35,7 @@ class PublishingSpec extends BasePluginSpecification { shadow 'shadow:b:1.0' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { archiveClassifier = '' archiveBaseName = 'maven-all' } @@ -116,7 +116,7 @@ class PublishingSpec extends BasePluginSpecification { } } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { archiveClassifier = 'my-classifier' archiveExtension = 'my-ext' archiveBaseName = 'maven-all' @@ -149,7 +149,6 @@ class PublishingSpec extends BasePluginSpecification { version = "1.0" group = 'shadow' - repositories { maven { url = "${repo.uri}" } } publishing { repositories { maven { @@ -189,7 +188,7 @@ class PublishingSpec extends BasePluginSpecification { shadow project(':b') } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { archiveClassifier = '' archiveBaseName = 'maven-all' } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy index 0b7d9d004..8eb8c4d35 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy @@ -7,7 +7,43 @@ import java.util.jar.JarFile class RelocationSpec extends BasePluginSpecification { - @Issue('SHADOW-58') + def "auto relocate plugin dependencies"() { + given: + buildFile << """ + $shadowJar { + enableRelocation = true + } + + dependencies { + implementation 'junit:junit:3.8.2' + } + """.stripIndent() + + when: + run('shadowJar') + + then: + contains(output, [ + 'META-INF/MANIFEST.MF', + 'shadow/junit/textui/ResultPrinter.class', + 'shadow/junit/textui/TestRunner.class', + 'shadow/junit/framework/Assert.class', + 'shadow/junit/framework/AssertionFailedError.class', + 'shadow/junit/framework/ComparisonCompactor.class', + 'shadow/junit/framework/ComparisonFailure.class', + 'shadow/junit/framework/Protectable.class', + 'shadow/junit/framework/Test.class', + 'shadow/junit/framework/TestCase.class', + 'shadow/junit/framework/TestFailure.class', + 'shadow/junit/framework/TestListener.class', + 'shadow/junit/framework/TestResult$1.class', + 'shadow/junit/framework/TestResult.class', + 'shadow/junit/framework/TestSuite$1.class', + 'shadow/junit/framework/TestSuite.class' + ]) + } + + @Issue('https://github.com/GradleUp/shadow/issues/58') def "relocate dependency files"() { given: buildFile << """ @@ -15,7 +51,7 @@ class RelocationSpec extends BasePluginSpecification { implementation 'junit:junit:3.8.2' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { relocate 'junit.textui', 'a' relocate 'junit.framework', 'b' manifest { @@ -80,8 +116,7 @@ class RelocationSpec extends BasePluginSpecification { implementation 'junit:junit:3.8.2' } - // tag::relocateFilter[] - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { relocate('junit.textui', 'a') { exclude 'junit.textui.TestRunner' } @@ -89,7 +124,6 @@ class RelocationSpec extends BasePluginSpecification { include 'junit.framework.Test*' } } - // end::relocateFilter[] """.stripIndent() when: @@ -129,7 +163,10 @@ class RelocationSpec extends BasePluginSpecification { ]) } - @Issue(['SHADOW-55', 'SHADOW-53']) + @Issue([ + 'https://github.com/GradleUp/shadow/issues/55', + 'https://github.com/GradleUp/shadow/issues/53', + ]) def "remap class names for relocated files in project source"() { given: buildFile << """ @@ -137,11 +174,9 @@ class RelocationSpec extends BasePluginSpecification { implementation 'junit:junit:3.8.2' } - // tag::relocate[] - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { relocate 'junit.framework', 'shadow.junit' } - // end::relocate[] """.stripIndent() file('src/main/java/shadow/ShadowTest.java') << ''' @@ -178,13 +213,12 @@ class RelocationSpec extends BasePluginSpecification { classLoader.loadClass('shadow.ShadowTest') } - @Issue('SHADOW-61') + @Issue('https://github.com/GradleUp/shadow/issues/61') def "relocate does not drop dependency resources"() { given: 'Core project with dependency and resource' file('core/build.gradle') << """ apply plugin: 'java-library' - repositories { maven { url = "${repo.uri}" } } dependencies { api 'junit:junit:3.8.2' } """.stripIndent() @@ -200,13 +234,11 @@ class RelocationSpec extends BasePluginSpecification { and: 'App project with shadow, relocation, and project dependency' file('app/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':core') } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { relocate 'core', 'app.core' relocate 'junit.framework', 'app.junit.framework' } @@ -245,7 +277,10 @@ class RelocationSpec extends BasePluginSpecification { ]) } - @Issue(['SHADOW-93', 'SHADOW-114']) + @Issue([ + 'https://github.com/GradleUp/shadow/issues/93', + 'https://github.com/GradleUp/shadow/issues/114', + ]) def "relocate resource files"() { given: repo.module('shadow', 'dep', '1.0') @@ -263,7 +298,7 @@ class RelocationSpec extends BasePluginSpecification { implementation 'shadow:dep:1.0' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { relocate 'foo', 'bar' } """.stripIndent() @@ -286,26 +321,18 @@ class RelocationSpec extends BasePluginSpecification { ]) } - @Issue("SHADOW-294") + @Issue("https://github.com/GradleUp/shadow/issues/294") def "does not error on relocating java9 classes"() { given: buildFile << """ - repositories { - mavenCentral() - maven { - url = 'https://repository.mapr.com/nexus/content/groups/mapr-public/releases' - } - } - dependencies { implementation 'org.slf4j:slf4j-api:1.7.21' implementation group: 'io.netty', name: 'netty-all', version: '4.0.23.Final' implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '2.5.0' implementation group: 'org.apache.zookeeper', name: 'zookeeper', version: '3.4.6' - implementation group: 'org.hbase', name: 'asynchbase', version: '1.7.0-mapr-1603' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { zip64 = true relocate 'com.google.protobuf', 'shaded.com.google.protobuf' relocate 'io.netty', 'shaded.io.netty' diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index 623056ca8..139c6d356 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -77,7 +77,7 @@ class ShadowPluginSpec extends BasePluginSpecification { implementation files('${escapedPath(one)}') } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { mergeServiceFiles() } """.stripIndent() @@ -101,7 +101,7 @@ class ShadowPluginSpec extends BasePluginSpecification { implementation 'junit:junit:3.8.2' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { mergeServiceFiles() } """.stripIndent() @@ -118,7 +118,7 @@ class ShadowPluginSpec extends BasePluginSpecification { URL project = this.class.classLoader.getResource('test-project-1.0-SNAPSHOT.jar') buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${artifact.path}') from('${project.path}') } @@ -141,14 +141,12 @@ class ShadowPluginSpec extends BasePluginSpecification { buildFile << """ dependencies { implementation 'junit:junit:3.8.2' } - // tag::rename[] - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { archiveBaseName = 'shadow' archiveClassifier = null archiveVersion = null archiveVersion.convention(null) } - // end::rename[] """.stripIndent() when: @@ -174,7 +172,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -187,10 +185,8 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -226,7 +222,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -241,14 +237,12 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { minimize() } - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -284,7 +278,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -294,16 +288,14 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { minimize { exclude(dependency('junit:junit:.*')) } } - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -338,7 +330,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + """.stripIndent() file('server/src/main/java/server/Server.java') << """ @@ -347,16 +339,14 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { minimize { exclude(project(':client')) } } - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -392,7 +382,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -402,16 +392,14 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { minimize { exclude(project(':client')) } } - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -445,7 +433,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -455,16 +443,14 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { minimize { exclude(project(':client')) } } - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -505,7 +491,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('lib/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + """.stripIndent() file('api/src/main/java/api/Entity.java') << """ @@ -521,7 +507,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('api/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + dependencies { implementation 'junit:junit:3.8.2' implementation project(':lib') @@ -535,14 +521,12 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('impl/build.gradle') << """ - apply plugin: 'java-library' - apply plugin: 'com.gradleup.shadow' + ${getDefaultBuildScript('java-library')} - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { minimize() } - repositories { maven { url = "${repo.uri}" } } dependencies { api project(':api') } """.stripIndent() @@ -584,7 +568,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('lib/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + """.stripIndent() file('api/src/main/java/api/Entity.java') << """ @@ -600,7 +584,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('api/build.gradle') << """ apply plugin: 'java-library' - repositories { maven { url = "${repo.uri}" } } + dependencies { api project(':lib') } """.stripIndent() @@ -611,14 +595,12 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('impl/build.gradle') << """ - apply plugin: 'java-library' - apply plugin: 'com.gradleup.shadow' + ${getDefaultBuildScript('java-library')} - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { minimize() } - repositories { maven { url = "${repo.uri}" } } dependencies { api project(':api') } """.stripIndent() @@ -650,12 +632,11 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('client/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' - repositories { maven { url = "${repo.uri}" } } + $defaultBuildScript + dependencies { implementation 'junit:junit:3.8.2' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { relocate 'junit.framework', 'client.junit.framework' } """.stripIndent() @@ -672,7 +653,6 @@ class ShadowPluginSpec extends BasePluginSpecification { file('server/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(path: ':client', configuration: 'shadow') } """.stripIndent() @@ -707,12 +687,11 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('client/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' - repositories { maven { url = "${repo.uri}" } } + $defaultBuildScript + dependencies { implementation 'junit:junit:3.8.2' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { relocate 'junit.framework', 'client.junit.framework' } """.stripIndent() @@ -727,10 +706,8 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(path: ':client', configuration: 'shadow') } """.stripIndent() @@ -829,7 +806,7 @@ class ShadowPluginSpec extends BasePluginSpecification { .insertFile('runtimeOnly.properties', 'runtimeOnly') .publish() - buildFile.text = getDefaultBuildScript('java-library') + buildFile.text = getDefaultBuildScript('java-library', true, true) buildFile << """ dependencies { api 'shadow:api:1.0' @@ -917,24 +894,20 @@ class ShadowPluginSpec extends BasePluginSpecification { assert attributes.getValue('Class-Path') == null } - @Issue('SHADOW-65') + @Issue('https://github.com/GradleUp/shadow/issues/65') def "add shadow configuration to Class-Path in Manifest"() { given: buildFile << """ - // tag::shadowConfig[] dependencies { shadow 'junit:junit:3.8.2' } - // end::shadowConfig[] - // tag::jarManifest[] jar { manifest { attributes 'Class-Path': '/libs/a.jar' } } - // end::jarManifest[] """.stripIndent() when: @@ -943,7 +916,7 @@ class ShadowPluginSpec extends BasePluginSpecification { then: assert output.exists() - and: 'SHADOW-65 - combine w/ existing Class-Path' + and: 'https://github.com/GradleUp/shadow/issues/65 - combine w/ existing Class-Path' JarFile jar = new JarFile(output) Attributes attributes = jar.manifest.getMainAttributes() String classpath = attributes.getValue('Class-Path') @@ -951,7 +924,7 @@ class ShadowPluginSpec extends BasePluginSpecification { } - @Issue('SHADOW-92') + @Issue('https://github.com/GradleUp/shadow/issues/92') def "do not include null value in Class-Path when jar file does not contain Class-Path"() { given: @@ -973,14 +946,14 @@ class ShadowPluginSpec extends BasePluginSpecification { } - @Issue('SHADOW-203') + @Issue('https://github.com/GradleUp/shadow/issues/203') def "support ZipCompression.STORED"() { given: buildFile << """ dependencies { shadow 'junit:junit:3.8.2' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { zip64 = true entryCompression = org.gradle.api.tasks.bundling.ZipEntryCompression.STORED } @@ -1003,7 +976,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('lib/build.gradle') << """ apply plugin: 'java' version = '1.0' - repositories { maven { url = "${repo.uri}" } } + """.stripIndent() file('api/src/main/java/api/UnusedEntity.java') << """ @@ -1014,7 +987,7 @@ class ShadowPluginSpec extends BasePluginSpecification { file('api/build.gradle') << """ apply plugin: 'java' version = '1.0' - repositories { maven { url = "${repo.uri}" } } + dependencies { implementation 'junit:junit:3.8.2' implementation project(':lib') @@ -1022,11 +995,10 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('impl/build.gradle') << """ - apply plugin: 'java-library' - apply plugin: 'com.gradleup.shadow' + ${getDefaultBuildScript('java-library')} version = '1.0' - repositories { maven { url = "${repo.uri}" } } + dependencies { api project(':api') } shadowJar.minimize() @@ -1044,7 +1016,7 @@ class ShadowPluginSpec extends BasePluginSpecification { ]) } - @Issue('SHADOW-143') + @Issue('https://github.com/GradleUp/shadow/issues/143') @Ignore("This spec requires > 15 minutes and > 8GB of disk space to run") def "check large zip files with zip64 enabled"() { given: @@ -1097,7 +1069,7 @@ class ShadowPluginSpec extends BasePluginSpecification { } } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { zip64 = true } @@ -1133,10 +1105,6 @@ class ShadowPluginSpec extends BasePluginSpecification { mainClass.set('myapp.Main') } - repositories { - mavenCentral() - } - runShadow { args 'foo' } @@ -1162,7 +1130,7 @@ class ShadowPluginSpec extends BasePluginSpecification { @Issue("https://github.com/GradleUp/shadow/pull/459") def 'exclude gradleApi() by default'() { given: - buildFile.text = getDefaultBuildScript('java-gradle-plugin') + buildFile.text = getDefaultBuildScript('java-gradle-plugin', true, true) file('src/main/java/my/plugin/MyPlugin.java') << """ package my.plugin; diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy index 1e3686980..4d5fa772d 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy @@ -28,11 +28,9 @@ class TransformerSpec extends BasePluginSpecification { buildFile << """ import ${ServiceFileTransformer.name} - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(ServiceFileTransformer) { exclude 'META-INF/services/com.acme.*' } @@ -68,11 +66,9 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() buildFile << """ import ${ServiceFileTransformer.name} - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(ServiceFileTransformer) { path = 'META-INF/foo' } @@ -108,11 +104,9 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() .write() buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { mergeServiceFiles { exclude 'META-INF/services/com.acme.*' } @@ -161,11 +155,9 @@ com.mysql.jdbc.Driver'''.stripIndent()) .write() buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { mergeServiceFiles() relocate('org.apache', 'myapache') { exclude 'org.apache.axis.components.compiler.Jikes' @@ -213,11 +205,9 @@ org.mortbay.log.Factory'''.stripIndent() 'two # NOTE: No newline terminates this line/file').write() buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { mergeServiceFiles('META-INF/foo') } """.stripIndent() @@ -236,7 +226,10 @@ org.mortbay.log.Factory'''.stripIndent() two # NOTE: No newline terminates this line/file'''.stripIndent() } - @Issue(['SHADOW-70', 'SHADOW-71']) + @Issue([ + 'https://github.com/GradleUp/shadow/issues/70', + 'https://github.com/GradleUp/shadow/issues/-71', + ]) def 'apply transformers to project resources'() { given: def one = buildJar('one.jar').insert('META-INF/services/shadow.Shadow', @@ -251,7 +244,7 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() implementation files('${escapedPath(one)}') } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { mergeServiceFiles() } """.stripIndent() @@ -284,11 +277,9 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() buildFile << """ import ${AppendingTransformer.name} - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(AppendingTransformer) { resource = 'test.properties' } @@ -319,11 +310,9 @@ two # NOTE: No newline terminates this line/file 'two # NOTE: No newline terminates this line/file').write() buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { append('test.properties') } """.stripIndent() @@ -400,7 +389,7 @@ two # NOTE: No newline terminates this line/file } } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { manifest { attributes 'Test-Entry': 'PASSED' attributes 'New-Entry': 'NEW' @@ -448,12 +437,9 @@ two # NOTE: No newline terminates this line/file buildFile << """ import ${XmlAppendingTransformer.name} - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(xml1)}') from('${escapedPath(xml2)}') - } - - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(XmlAppendingTransformer) { resource = 'properties.xml' } @@ -478,7 +464,7 @@ two # NOTE: No newline terminates this line/file '''.stripIndent() } - @Issue('SHADOW-82') + @Issue('https://github.com/GradleUp/shadow/issues/82') def 'shadow.manifest leaks to jar.manifest'() { given: File main = file('src/main/java/shadow/Main.java') @@ -499,7 +485,7 @@ two # NOTE: No newline terminates this line/file } } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { manifest { attributes 'Test-Entry': 'PASSED' attributes 'New-Entry': 'NEW' @@ -538,7 +524,7 @@ two # NOTE: No newline terminates this line/file jis2?.close() } - @Issue('SHADOW-82') + @Issue('https://github.com/GradleUp/shadow/issues/82') def 'shadow manifest leaks to jar manifest'() { given: File main = file('src/main/java/shadow/Main.java') @@ -559,7 +545,7 @@ two # NOTE: No newline terminates this line/file } } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { manifest { attributes 'Test-Entry': 'PASSED' attributes 'New-Entry': 'NEW' @@ -616,12 +602,9 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( buildFile << """ import ${GroovyExtensionModuleTransformer.name} - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(GroovyExtensionModuleTransformer) } """.stripIndent() @@ -660,12 +643,9 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( buildFile << """ import ${GroovyExtensionModuleTransformer.name} - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(GroovyExtensionModuleTransformer) } """.stripIndent() @@ -704,11 +684,9 @@ extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { mergeGroovyExtensionModules() } """.stripIndent() @@ -738,13 +716,13 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( buildFile << """ import com.github.jengelman.gradle.plugins.shadow.transformers.${transformer} - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { transform(${transformer})${configuration} } """.stripIndent() when: - run('shadowJar', '--warning-mode=all') + run('shadowJar') then: assert output.exists() diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy index 15a08ced3..da4afc8fc 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy @@ -29,7 +29,7 @@ abstract class AbstractCachingSpec extends BasePluginSpecification { } void changeConfigurationTo(String content) { - buildFile.text = defaultBuildScript + buildFile.text = getDefaultBuildScript('java', true, true) buildFile << content } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy index 460fcc108..69bb06c88 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy @@ -2,7 +2,11 @@ package com.github.jengelman.gradle.plugins.shadow.caching class MinimizationCachingSpec extends AbstractCachingSpec { File output - String shadowJarTask = ":server:shadowJar" + + @Override + String getShadowJarTask() { + return ":server:shadowJar" + } /** * Ensure that we get a cache miss when minimization is added and that caching works with minimization @@ -20,7 +24,7 @@ class MinimizationCachingSpec extends AbstractCachingSpec { file('client/build.gradle') << """ apply plugin: 'java' - repositories { maven { url = "${repo.uri}" } } + dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -30,10 +34,8 @@ class MinimizationCachingSpec extends AbstractCachingSpec { """.stripIndent() file('server/build.gradle') << """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' + $defaultBuildScript - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() @@ -51,17 +53,13 @@ class MinimizationCachingSpec extends AbstractCachingSpec { ]) when: - file('server/build.gradle').text = """ - apply plugin: 'java' - apply plugin: 'com.gradleup.shadow' - - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + file('server/build.gradle') << """ + $shadowJar { minimize { exclude(dependency('junit:junit:.*')) } } - repositories { maven { url = "${repo.uri}" } } dependencies { implementation project(':client') } """.stripIndent() assertShadowJarExecutes() diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy index 9a43e7270..1fa573932 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy @@ -32,7 +32,7 @@ class RelocationCachingSpec extends AbstractCachingSpec { changeConfigurationTo """ dependencies { implementation 'junit:junit:3.8.2' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { relocate 'junit.framework', 'foo.junit.framework' } """ diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy index def98418c..0c3b4efc4 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy @@ -11,7 +11,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { URL project = this.class.classLoader.getResource('test-project-1.0-SNAPSHOT.jar') buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${artifact.path}') from('${project.path}') } @@ -31,7 +31,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { when: changeConfigurationTo """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${artifact.path}') } """ @@ -50,7 +50,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { URL project = this.class.classLoader.getResource('test-project-1.0-SNAPSHOT.jar') buildFile << """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { from('${artifact.path}') from('${project.path}') } @@ -64,7 +64,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { when: changeConfigurationTo """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { archiveBaseName = "foo" from('${artifact.path}') from('${project.path}') @@ -85,7 +85,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { buildFile << """ dependencies { implementation 'junit:junit:3.8.2' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { exclude 'junit/*' } """.stripIndent() @@ -120,7 +120,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { changeConfigurationTo """ dependencies { implementation 'junit:junit:3.8.2' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { include 'server/*' exclude '*/Util.*' } @@ -186,7 +186,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { changeConfigurationTo """ dependencies { implementation 'junit:junit:3.8.2' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { dependencies { exclude(dependency('junit:junit')) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy index a8507c9af..b937815c1 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy @@ -46,7 +46,7 @@ class TransformCachingSpec extends AbstractCachingSpec { } } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { notCompatibleWithConfigurationCache('CustomTransformer is not cacheable') transform(CustomTransformer) } @@ -95,7 +95,7 @@ class TransformCachingSpec extends AbstractCachingSpec { when: // Add a transform changeConfigurationTo """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { transform(${ServiceFileTransformer.name}) { path = 'META-INF/foo' } @@ -121,7 +121,7 @@ class TransformCachingSpec extends AbstractCachingSpec { when: // Change the transform configuration changeConfigurationTo """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { transform(${ServiceFileTransformer.name}) { path = 'META-INF/bar' } @@ -170,7 +170,7 @@ class TransformCachingSpec extends AbstractCachingSpec { when: // Add a transform changeConfigurationTo """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { transform(${AppendingTransformer.name}) { resource = 'foo/bar.properties' } @@ -200,7 +200,7 @@ class TransformCachingSpec extends AbstractCachingSpec { assert file('src/main/resources/foo/bar.properties').delete() file('src/main/resources/foo/baz.properties') << "foo=baz" changeConfigurationTo """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { transform(${AppendingTransformer.name}) { resource = 'foo/baz.properties' } @@ -251,7 +251,7 @@ class TransformCachingSpec extends AbstractCachingSpec { when: // Add a transform changeConfigurationTo """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { transform(${XmlAppendingTransformer.name}) { resource = 'foo/bar.xml' } @@ -281,7 +281,7 @@ class TransformCachingSpec extends AbstractCachingSpec { assert file('src/main/resources/foo/bar.xml').delete() file('src/main/resources/foo/baz.xml') << "baz" changeConfigurationTo """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { transform(${AppendingTransformer.name}) { resource = 'foo/baz.xml' } @@ -331,7 +331,7 @@ class TransformCachingSpec extends AbstractCachingSpec { when: // Add a transform changeConfigurationTo """ - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + $shadowJar { transform(${GroovyExtensionModuleTransformer.name}) } """ From 6c3341c81a247d4f0e1b55ef6dad54222d828ba4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 28 Dec 2024 03:39:47 -0500 Subject: [PATCH 116/941] Migrate functional tests to Kotlin and Junit part 1 (#1121) * Copy jars * Add BasePluginTest * Convert RelocationSpec * Workaround for junit5 issue 2811 * Convert ApplicationSpec * Tweak block slots * Replace hardcodes * Cleanups * Convert ConfigurationCacheSpec * Convert FilteringSpec * Remove unnecessary line appending * Cleanups --- build.gradle.kts | 2 + .../plugins/shadow/ApplicationSpec.groovy | 249 ----------- .../shadow/ConfigurationCacheSpec.groovy | 166 ------- .../plugins/shadow/FilteringSpec.groovy | 414 ------------------ .../plugins/shadow/RelocationSpec.groovy | 348 --------------- .../gradle/plugins/shadow/ApplicationTest.kt | 129 ++++++ .../gradle/plugins/shadow/BasePluginTest.kt | 262 +++++++++++ .../plugins/shadow/ConfigurationCacheSpec.kt | 154 +++++++ .../gradle/plugins/shadow/FilteringTest.kt | 288 ++++++++++++ .../gradle/plugins/shadow/RelocationTest.kt | 390 +++++++++++++++++ src/intiTest/resources/junit-3.8.2.jar | Bin 0 -> 120640 bytes .../resources/test-artifact-1.0-SNAPSHOT.jar | Bin 0 -> 3115 bytes .../resources/test-project-1.0-SNAPSHOT.jar | Bin 0 -> 3906 bytes 13 files changed, 1225 insertions(+), 1177 deletions(-) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt create mode 100644 src/intiTest/resources/junit-3.8.2.jar create mode 100644 src/intiTest/resources/test-artifact-1.0-SNAPSHOT.jar create mode 100644 src/intiTest/resources/test-project-1.0-SNAPSHOT.jar diff --git a/build.gradle.kts b/build.gradle.kts index 82187aafe..4fd1f40de 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -93,6 +93,8 @@ dependencies { intiTestImplementation(libs.okio) intiTestImplementation(libs.apache.maven.modelBuilder) intiTestImplementation(libs.apache.maven.repositoryMetadata) + // TODO: this will be removed after we migrated all functional tests to Kotlin. + intiTestImplementation(sourceSets.main.get().output) lintChecks(libs.androidx.gradlePluginLints) lintChecks(libs.assertk.lint) diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy deleted file mode 100644 index 939e3cbb6..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ApplicationSpec.groovy +++ /dev/null @@ -1,249 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import org.apache.tools.zip.ZipFile -import org.gradle.testkit.runner.BuildResult -import spock.lang.Issue - -import java.util.jar.Attributes -import java.util.jar.JarFile - -class ApplicationSpec extends BasePluginSpecification { - - def 'integration with application plugin'() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - - file('src/main/java/myapp/Main.java') << """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.stripIndent() - - buildFile << """ - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - - dependencies { - implementation 'shadow:a:1.0' - } - - runShadow { - args 'foo' - } - """.stripIndent() - - settingsFile << "rootProject.name = 'myapp'" - - when: - BuildResult result = run('runShadow') - - then: 'tests that runShadow executed and exited' - assert result.output.contains('TestApp: Hello World! (foo)') - - and: 'Check that the proper jar file was installed' - File installedJar = getFile('build/install/myapp-shadow/lib/myapp-1.0-all.jar') - assert installedJar.exists() - - and: 'And that jar file as the correct files in it' - contains(installedJar, ['a.properties', 'a2.properties', 'myapp/Main.class']) - - and: 'Check the manifest attributes in the jar file are correct' - JarFile jar = new JarFile(installedJar) - Attributes attributes = jar.manifest.mainAttributes - assert attributes.getValue('Main-Class') == 'myapp.Main' - - then: 'Check that the start scripts is written out and has the correct Java invocation' - File startScript = getFile('build/install/myapp-shadow/bin/myapp') - assert startScript.exists() - assert startScript.text.contains("CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar") - assert startScript.text.contains("-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"") - assert startScript.text.contains("exec \"\$JAVACMD\" \"\$@\"") - - cleanup: - jar?.close() - } - - def 'integration with application plugin and java toolchains'() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - - file('src/main/java/myapp/Main.java') << """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.stripIndent() - - buildFile << """ - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - - dependencies { - implementation 'shadow:a:1.0' - } - - java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } - } - - runShadow { - args 'foo' - doFirst { - logger.lifecycle("Running application with JDK \${it.javaLauncher.get().metadata.languageVersion.asInt()}") - } - } - """.stripIndent() - - settingsFile.write """ - plugins { - // https://docs.gradle.org/8.0.1/userguide/toolchains.html#sub:download_repositories - id("org.gradle.toolchains.foojay-resolver-convention") version("0.7.0") - } - - rootProject.name = 'myapp' - """.stripIndent() + getSettingsBuildScript(false) - - when: - BuildResult result = run('runShadow') - - then: 'tests that runShadow executed and exited' - assert result.output.contains('Running application with JDK 17') - assert result.output.contains('TestApp: Hello World! (foo)') - - and: 'Check that the proper jar file was installed' - File installedJar = getFile('build/install/myapp-shadow/lib/myapp-1.0-all.jar') - assert installedJar.exists() - - and: 'And that jar file as the correct files in it' - contains(installedJar, ['a.properties', 'a2.properties', 'myapp/Main.class']) - - and: 'Check the manifest attributes in the jar file are correct' - JarFile jar = new JarFile(installedJar) - Attributes attributes = jar.manifest.mainAttributes - assert attributes.getValue('Main-Class') == 'myapp.Main' - - then: 'Check that the start scripts is written out and has the correct Java invocation' - File startScript = getFile('build/install/myapp-shadow/bin/myapp') - assert startScript.exists() - assert startScript.text.contains("CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar") - assert startScript.text.contains("-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"") - assert startScript.text.contains("exec \"\$JAVACMD\" \"\$@\"") - - cleanup: - jar?.close() - } - - @Issue('https://github.com/GradleUp/shadow/issues/89') - def 'shadow application distributions should use shadow jar'() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - - file('src/main/java/myapp/Main.java') << """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.stripIndent() - - buildFile << """ - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - - dependencies { - shadow 'shadow:a:1.0' - } - - runShadow { - args 'foo' - } - """.stripIndent() - - settingsFile << "rootProject.name = 'myapp'" - - when: - run('shadowDistZip') - - then: 'Check that the distribution zip was created' - File zip = getFile('build/distributions/myapp-shadow-1.0.zip') - assert zip.exists() - - and: 'Check that the zip contains the correct library files & scripts' - ZipFile zipFile = new ZipFile(zip) - println zipFile.entries.collect { it.name } - assert zipFile.entries.find { it.name == 'myapp-shadow-1.0/lib/myapp-1.0-all.jar' } - assert zipFile.entries.find { it.name == 'myapp-shadow-1.0/lib/a-1.0.jar' } - - cleanup: - zipFile?.close() - } - - @Issue('https://github.com/GradleUp/shadow/issues/90') - def 'installShadow does not execute dependent shadow task'() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - - file('src/main/java/myapp/Main.java') << """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.stripIndent() - - buildFile << """ - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - - dependencies { - implementation 'shadow:a:1.0' - } - - runShadow { - args 'foo' - } - """.stripIndent() - - settingsFile << "rootProject.name = 'myapp'" - - when: - run(ShadowApplicationPlugin.SHADOW_INSTALL_TASK_NAME) - - then: 'Check that the proper jar file was installed' - File installedJar = getFile('build/install/myapp-shadow/lib/myapp-1.0-all.jar') - assert installedJar.exists() - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy deleted file mode 100644 index 2391e809d..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.groovy +++ /dev/null @@ -1,166 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -class ConfigurationCacheSpec extends BasePluginSpecification { - - @Override - def setup() { - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() - - buildFile << """ - dependencies { - implementation 'shadow:a:1.0' - implementation 'shadow:b:1.0' - } - """.stripIndent() - } - - def "supports configuration cache"() { - given: - file('src/main/java/myapp/Main.java') << """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.stripIndent() - - buildFile << """ - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - - dependencies { - implementation 'shadow:a:1.0' - } - - runShadow { - args 'foo' - } - """.stripIndent() - - settingsFile << "rootProject.name = 'myapp'" - - when: - run('shadowJar') - def result = run('shadowJar') - - then: - result.output.contains("Reusing configuration cache.") - } - - def "configuration caching supports includes"() { - given: - buildFile << """ - $shadowJar { - exclude 'a2.properties' - } - """.stripIndent() - - when: - run('shadowJar') - output.delete() - def result = run('shadowJar') - - then: - contains(output, ['a.properties', 'b.properties']) - - and: - doesNotContain(output, ['a2.properties']) - result.output.contains("Reusing configuration cache.") - } - - def "configuration caching supports minimize"() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - and: - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - file('client/build.gradle') << """ - apply plugin: 'java' - - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - and: - file('server/src/main/java/server/Server.java') << """ - package server; - public class Server {} - """.stripIndent() - file('server/build.gradle') << """ - $defaultBuildScript - - $shadowJar { - minimize { - exclude(dependency('junit:junit:.*')) - } - } - - dependencies { implementation project(':client') } - """.stripIndent() - - and: - def output = getFile('server/build/libs/server-all.jar') - - when: - run('shadowJar') - output.delete() - def result = run('shadowJar') - - then: - output.exists() - contains(output, [ - 'server/Server.class', - 'junit/framework/Test.class' - ]) - doesNotContain(output, ['client/Client.class']) - - and: - result.output.contains("Reusing configuration cache.") - } - - def "configuration caching of configurations is up-to-date"() { - given: - file('settings.gradle') << """ - include 'lib' - """.stripIndent() - - and: - file('lib/src/main/java/lib/Lib.java') << """ - package lib; - public class Lib {} - """.stripIndent() - file('lib/build.gradle') << """ - $defaultBuildScript - - dependencies { - implementation "junit:junit:3.8.2" - } - - $shadowJar { - configurations = [project.configurations.compileClasspath] - } - - """.stripIndent() - - when: - run('shadowJar') - def result = run('shadowJar') - - then: - result.output.contains(":lib:shadowJar UP-TO-DATE") - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy deleted file mode 100644 index b365e6da2..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/FilteringSpec.groovy +++ /dev/null @@ -1,414 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.TaskOutcome -import spock.lang.Issue - -class FilteringSpec extends BasePluginSpecification { - - @Override - def setup() { - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() - - buildFile << """ - dependencies { - implementation 'shadow:a:1.0' - implementation 'shadow:b:1.0' - } - """.stripIndent() - - } - - def 'include all dependencies'() { - when: - run('shadowJar') - - then: - contains(output, ['a.properties', 'a2.properties', 'b.properties']) - } - - def 'exclude files'() { - given: - buildFile << """ - $shadowJar { - exclude 'a2.properties' - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, ['a.properties', 'b.properties']) - - and: - doesNotContain(output, ['a2.properties']) - } - - def "exclude dependency"() { - given: - repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() - repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() - - buildFile << """ - dependencies { - implementation 'shadow:d:1.0' - } - - $shadowJar { - dependencies { - exclude(dependency('shadow:d:1.0')) - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, ['a.properties', 'a2.properties', 'b.properties', 'c.properties']) - - and: - doesNotContain(output, ['d.properties']) - } - - @Issue('https://github.com/GradleUp/shadow/issues/83') - def "exclude dependency using wildcard syntax"() { - given: - repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() - repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() - - buildFile << """ - dependencies { - implementation 'shadow:d:1.0' - } - - $shadowJar { - dependencies { - exclude(dependency('shadow:d:.*')) - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, ['a.properties', 'a2.properties', 'b.properties', 'c.properties']) - - and: - doesNotContain(output, ['d.properties']) - } - - @Issue("https://github.com/GradleUp/shadow/issues/54") - def "dependency exclusions affect UP-TO-DATE check"() { - given: - repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() - repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() - - buildFile << """ - dependencies { - implementation 'shadow:d:1.0' - } - - $shadowJar { - dependencies { - exclude(dependency('shadow:d:1.0')) - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, ['a.properties', 'a2.properties', 'b.properties', 'c.properties']) - - and: - doesNotContain(output, ['d.properties']) - - when: 'Update build file shadowJar dependency exclusion' - buildFile.text = buildFile.text.replace('exclude(dependency(\'shadow:d:1.0\'))', - 'exclude(dependency(\'shadow:c:1.0\'))') - - BuildResult result = run('shadowJar') - - then: - assert result.task(':shadowJar').outcome == TaskOutcome.SUCCESS - - and: - contains(output, ['a.properties', 'a2.properties', 'b.properties', 'd.properties']) - - and: - doesNotContain(output, ['c.properties']) - } - - @Issue("https://github.com/GradleUp/shadow/issues/62") - def "project exclusions affect UP-TO-DATE check"() { - given: - repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() - repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() - - buildFile << """ - dependencies { - implementation 'shadow:d:1.0' - } - - $shadowJar { - dependencies { - exclude(dependency('shadow:d:1.0')) - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, ['a.properties', 'a2.properties', 'b.properties', 'c.properties']) - - and: - doesNotContain(output, ['d.properties']) - - when: 'Update build file shadowJar dependency exclusion' - buildFile.text = buildFile.text.replace('exclude(dependency(\'shadow:d:1.0\'))', - 'exclude \'a.properties\'') - - BuildResult result = run('shadowJar') - - then: - assert result.task(':shadowJar').outcome == TaskOutcome.SUCCESS - - and: - contains(output, ['a2.properties', 'b.properties', 'c.properties', 'd.properties']) - - and: - doesNotContain(output, ['a.properties']) - } - - def "include dependency, excluding all others"() { - given: - repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .publish() - repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() - - file('src/main/java/shadow/Passed.java') << ''' - package shadow; - public class Passed {} - '''.stripIndent() - - buildFile << """ - dependencies { - implementation 'shadow:d:1.0' - } - - $shadowJar { - dependencies { - include(dependency('shadow:d:1.0')) - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, ['d.properties', 'shadow/Passed.class']) - - and: - doesNotContain(output, ['a.properties', 'a2.properties', 'b.properties', 'c.properties']) - } - - def 'filter project dependencies'() { - given: - buildFile.text = '' - - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - - file('client/build.gradle') << """ - ${getDefaultBuildScript('java', false, true)} - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - import client.Client; - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - ${getDefaultBuildScript('java', false, true)} - - dependencies { - implementation project(':client') - } - - $shadowJar { - dependencies { - exclude(project(':client')) - } - } - """.stripIndent() - - File serverOutput = getFile('server/build/libs/server-1.0-all.jar') - - when: - run(':server:shadowJar') - - then: - serverOutput.exists() - doesNotContain(serverOutput, [ - 'client/Client.class', - ]) - - and: - contains(serverOutput, ['server/Server.class', 'junit/framework/Test.class']) - } - - def 'exclude a transitive project dependency'() { - given: - buildFile.text = '' - - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - - file('client/build.gradle') << """ - ${getDefaultBuildScript('java', false, true)} - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - import client.Client; - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - ${getDefaultBuildScript('java', false, true)} - dependencies { implementation project(':client') } - - $shadowJar { - dependencies { - exclude(dependency { - it.moduleGroup == 'junit' - }) - } - } - """.stripIndent() - - File serverOutput = getFile('server/build/libs/server-1.0-all.jar') - - when: - run(':server:shadowJar') - - then: - serverOutput.exists() - doesNotContain(serverOutput, [ - 'junit/framework/Test.class' - ]) - - and: - contains(serverOutput, [ - 'client/Client.class', - 'server/Server.class']) - } - - //http://mail-archives.apache.org/mod_mbox/ant-user/200506.mbox/%3C001d01c57756$6dc35da0$dc00a8c0@CTEGDOMAIN.COM%3E - def 'verify exclude precedence over include'() { - given: - buildFile << """ - $shadowJar { - include '*.jar' - include '*.properties' - exclude 'a2.properties' - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, ['a.properties', 'b.properties']) - - and: - doesNotContain(output, ['a2.properties']) - } - - @Issue("https://github.com/GradleUp/shadow/issues/69") - def "handle exclude with circular dependency"() { - given: - repo.module('shadow', 'c', '1.0') - .insertFile('c.properties', 'c') - .dependsOn('d') - .publish() - repo.module('shadow', 'd', '1.0') - .insertFile('d.properties', 'd') - .dependsOn('c') - .publish() - - buildFile << """ - dependencies { - implementation 'shadow:d:1.0' - } - - $shadowJar { - dependencies { - exclude(dependency('shadow:d:1.0')) - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, ['a.properties', 'a2.properties', 'b.properties', 'c.properties']) - - and: - doesNotContain(output, ['d.properties']) - } - -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy deleted file mode 100644 index 8eb8c4d35..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/RelocationSpec.groovy +++ /dev/null @@ -1,348 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import spock.lang.Issue - -import java.util.jar.Attributes -import java.util.jar.JarFile - -class RelocationSpec extends BasePluginSpecification { - - def "auto relocate plugin dependencies"() { - given: - buildFile << """ - $shadowJar { - enableRelocation = true - } - - dependencies { - implementation 'junit:junit:3.8.2' - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, [ - 'META-INF/MANIFEST.MF', - 'shadow/junit/textui/ResultPrinter.class', - 'shadow/junit/textui/TestRunner.class', - 'shadow/junit/framework/Assert.class', - 'shadow/junit/framework/AssertionFailedError.class', - 'shadow/junit/framework/ComparisonCompactor.class', - 'shadow/junit/framework/ComparisonFailure.class', - 'shadow/junit/framework/Protectable.class', - 'shadow/junit/framework/Test.class', - 'shadow/junit/framework/TestCase.class', - 'shadow/junit/framework/TestFailure.class', - 'shadow/junit/framework/TestListener.class', - 'shadow/junit/framework/TestResult$1.class', - 'shadow/junit/framework/TestResult.class', - 'shadow/junit/framework/TestSuite$1.class', - 'shadow/junit/framework/TestSuite.class' - ]) - } - - @Issue('https://github.com/GradleUp/shadow/issues/58') - def "relocate dependency files"() { - given: - buildFile << """ - dependencies { - implementation 'junit:junit:3.8.2' - } - - $shadowJar { - relocate 'junit.textui', 'a' - relocate 'junit.framework', 'b' - manifest { - attributes 'TEST-VALUE': 'FOO' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, [ - 'META-INF/MANIFEST.MF', - 'a/ResultPrinter.class', - 'a/TestRunner.class', - 'b/Assert.class', - 'b/AssertionFailedError.class', - 'b/ComparisonCompactor.class', - 'b/ComparisonFailure.class', - 'b/Protectable.class', - 'b/Test.class', - 'b/TestCase.class', - 'b/TestFailure.class', - 'b/TestListener.class', - 'b/TestResult$1.class', - 'b/TestResult.class', - 'b/TestSuite$1.class', - 'b/TestSuite.class' - ]) - - and: - doesNotContain(output, [ - 'junit/textui/ResultPrinter.class', - 'junit/textui/TestRunner.class', - 'junit/framework/Assert.class', - 'junit/framework/AssertionFailedError.class', - 'junit/framework/ComparisonCompactor.class', - 'junit/framework/ComparisonFailure.class', - 'junit/framework/Protectable.class', - 'junit/framework/Test.class', - 'junit/framework/TestCase.class', - 'junit/framework/TestFailure.class', - 'junit/framework/TestListener.class', - 'junit/framework/TestResult$1.class', - 'junit/framework/TestResult.class', - 'junit/framework/TestSuite$1.class', - 'junit/framework/TestSuite.class' - ]) - - and: 'Test that manifest file exists with contents' - JarFile jar = new JarFile(output) - Attributes attributes = jar.manifest.getMainAttributes() - String val = attributes.getValue('TEST-VALUE') - assert val == 'FOO' - } - - def "relocate dependency files with filtering"() { - given: - buildFile << """ - dependencies { - implementation 'junit:junit:3.8.2' - } - - $shadowJar { - relocate('junit.textui', 'a') { - exclude 'junit.textui.TestRunner' - } - relocate('junit.framework', 'b') { - include 'junit.framework.Test*' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, [ - 'a/ResultPrinter.class', - 'b/Test.class', - 'b/TestCase.class', - 'b/TestFailure.class', - 'b/TestListener.class', - 'b/TestResult$1.class', - 'b/TestResult.class', - 'b/TestSuite$1.class', - 'b/TestSuite.class' - ]) - - and: - doesNotContain(output, [ - 'a/TestRunner.class', - 'b/Assert.class', - 'b/AssertionFailedError.class', - 'b/ComparisonCompactor.class', - 'b/ComparisonFailure.class', - 'b/Protectable.class' - ]) - - and: - contains(output, [ - 'junit/textui/TestRunner.class', - 'junit/framework/Assert.class', - 'junit/framework/AssertionFailedError.class', - 'junit/framework/ComparisonCompactor.class', - 'junit/framework/ComparisonFailure.class', - 'junit/framework/Protectable.class' - ]) - } - - @Issue([ - 'https://github.com/GradleUp/shadow/issues/55', - 'https://github.com/GradleUp/shadow/issues/53', - ]) - def "remap class names for relocated files in project source"() { - given: - buildFile << """ - dependencies { - implementation 'junit:junit:3.8.2' - } - - $shadowJar { - relocate 'junit.framework', 'shadow.junit' - } - """.stripIndent() - - file('src/main/java/shadow/ShadowTest.java') << ''' - package shadow; - - import junit.framework.Test; - import junit.framework.TestResult; - public class ShadowTest implements Test { - public int countTestCases() { return 0; } - public void run(TestResult result) { } - } - '''.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, [ - 'shadow/ShadowTest.class', - 'shadow/junit/Test.class', - 'shadow/junit' - ]) - - and: - doesNotContain(output, [ - 'junit/framework', - 'junit/framework/Test.class' - ]) - - and: 'check that the class can be loaded. If the file was not relocated properly, we should get a NoDefClassFound' - // Isolated class loader with only the JVM system jars and the output jar from the test project - URLClassLoader classLoader = new URLClassLoader([output.toURI().toURL()] as URL[], - ClassLoader.systemClassLoader.parent) - classLoader.loadClass('shadow.ShadowTest') - } - - @Issue('https://github.com/GradleUp/shadow/issues/61') - def "relocate does not drop dependency resources"() { - given: 'Core project with dependency and resource' - file('core/build.gradle') << """ - apply plugin: 'java-library' - - dependencies { api 'junit:junit:3.8.2' } - """.stripIndent() - - file('core/src/main/resources/TEST') << 'TEST RESOURCE' - file('core/src/main/resources/test.properties') << 'name=test' - file('core/src/main/java/core/Core.java') << ''' - package core; - - import junit.framework.Test; - - public class Core {} - '''.stripIndent() - - and: 'App project with shadow, relocation, and project dependency' - file('app/build.gradle') << """ - $defaultBuildScript - - dependencies { implementation project(':core') } - - $shadowJar { - relocate 'core', 'app.core' - relocate 'junit.framework', 'app.junit.framework' - } - """.stripIndent() - - file('app/src/main/resources/APP-TEST') << 'APP TEST RESOURCE' - file('app/src/main/java/app/App.java') << ''' - package app; - - import core.Core; - import junit.framework.Test; - - public class App {} - '''.stripIndent() - - and: 'Configure multi-project build' - settingsFile << ''' - include 'core', 'app' - '''.stripIndent() - - when: - run(':app:shadowJar') - - then: - File appOutput = getFile('app/build/libs/app-all.jar') - assert appOutput.exists() - - and: - contains(appOutput, [ - 'TEST', - 'APP-TEST', - 'test.properties', - 'app/core/Core.class', - 'app/App.class', - 'app/junit/framework/Test.class' - ]) - } - - @Issue([ - 'https://github.com/GradleUp/shadow/issues/93', - 'https://github.com/GradleUp/shadow/issues/114', - ]) - def "relocate resource files"() { - given: - repo.module('shadow', 'dep', '1.0') - .insertFile('foo/dep.properties', 'c') - .publish() - file('src/main/java/foo/Foo.java') << ''' - package foo; - - class Foo {} - '''.stripIndent() - file('src/main/resources/foo/foo.properties') << 'name=foo' - - buildFile << """ - dependencies { - implementation 'shadow:dep:1.0' - } - - $shadowJar { - relocate 'foo', 'bar' - } - """.stripIndent() - - when: - run('shadowJar') - - then: - contains(output, [ - 'bar/Foo.class', - 'bar/foo.properties', - 'bar/dep.properties' - ]) - - and: - doesNotContain(output, [ - 'foo/Foo.class', - 'foo/foo.properties', - 'foo/dep.properties' - ]) - } - - @Issue("https://github.com/GradleUp/shadow/issues/294") - def "does not error on relocating java9 classes"() { - given: - buildFile << """ - dependencies { - implementation 'org.slf4j:slf4j-api:1.7.21' - implementation group: 'io.netty', name: 'netty-all', version: '4.0.23.Final' - implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '2.5.0' - implementation group: 'org.apache.zookeeper', name: 'zookeeper', version: '3.4.6' - } - - $shadowJar { - zip64 = true - relocate 'com.google.protobuf', 'shaded.com.google.protobuf' - relocate 'io.netty', 'shaded.io.netty' - } - """.stripIndent() - - when: - run('shadowJar') - - then: - noExceptionThrown() - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt new file mode 100644 index 000000000..5160d4a04 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt @@ -0,0 +1,129 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.containsAtLeast +import assertk.assertions.exists +import assertk.assertions.isEqualTo +import java.util.jar.JarFile +import kotlin.io.path.appendText +import kotlin.io.path.readText +import kotlin.io.path.writeText +import org.apache.tools.zip.ZipFile +import org.junit.jupiter.api.Test + +class ApplicationTest : BasePluginTest() { + + @Test + fun integrationWithApplicationPluginAndJavaToolchains() { + prepare( + projectBlock = """ + java { + toolchain.languageVersion = JavaLanguageVersion.of(17) + } + """.trimIndent(), + settingsBlock = """ + plugins { + id('org.gradle.toolchains.foojay-resolver-convention') version '0.7.0' + } + """.trimIndent(), + runShadowBlock = """ + doFirst { + logger.lifecycle("Running application with JDK ${'$'}{it.javaLauncher.get().metadata.languageVersion.asInt()}") + } + """.trimIndent(), + ) + + val result = run(runShadowTask) + + assertThat(result.output).contains("Running application with JDK 17") + assertThat(result.output).contains("TestApp: Hello World! (foo)") + + val installedJar = path("build/install/myapp-shadow/lib/myapp-1.0-all.jar") + assertThat(installedJar).exists() + + assertContains(installedJar, listOf("a.properties", "a2.properties", "myapp/Main.class")) + + val jarFile = JarFile(installedJar.toFile()) + assertThat(jarFile.manifest.mainAttributes.getValue("Main-Class")) + .isEqualTo("myapp.Main") + + path("build/install/myapp-shadow/bin/myapp").let { startScript -> + assertThat(startScript).exists() + assertThat(startScript.readText()).contains("CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar") + assertThat(startScript.readText()).contains("-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"") + assertThat(startScript.readText()).contains("exec \"\$JAVACMD\" \"\$@\"") + } + } + + @Test + fun shadowApplicationDistributionsShouldUseShadowJar() { + prepare( + projectBlock = """ + dependencies { + shadow 'shadow:a:1.0' + } + """.trimIndent(), + ) + + run("shadowDistZip") + + val zip = path("build/distributions/myapp-shadow-1.0.zip") + assertThat(zip).exists() + + val entries = ZipFile(zip.toFile()).entries.toList().map { it.name } + assertThat(entries).containsAtLeast( + "myapp-shadow-1.0/lib/myapp-1.0-all.jar", + "myapp-shadow-1.0/lib/a-1.0.jar", + ) + } + + @Test + fun installShadowDoesNotExecuteDependentShadowTask() { + prepare() + + run(ShadowApplicationPlugin.SHADOW_INSTALL_TASK_NAME) + + assertThat(path("build/install/myapp-shadow/lib/myapp-1.0-all.jar")).exists() + } + + private fun prepare( + projectBlock: String = "", + settingsBlock: String = "", + runShadowBlock: String = "", + ) { + publishArtifactA() + path("src/main/java/myapp/Main.java").appendText( + """ + package myapp; + public class Main { + public static void main(String[] args) { + System.out.println("TestApp: Hello World! (" + args[0] + ")"); + } + } + """.trimIndent(), + ) + buildScript.appendText( + """ + apply plugin: 'application' + $projectBlock + application { + mainClass = 'myapp.Main' + } + dependencies { + implementation 'shadow:a:1.0' + } + $runShadow { + args 'foo' + $runShadowBlock + } + """.trimIndent(), + ) + settingsScript.writeText( + getSettingsBuildScript( + startBlock = settingsBlock, + endBlock = "rootProject.name = 'myapp'", + ), + ) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt new file mode 100644 index 000000000..ff828078a --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -0,0 +1,262 @@ +package com.github.jengelman.gradle.plugins.shadow + +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository +import java.nio.file.Path +import java.util.jar.JarFile +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.appendText +import kotlin.io.path.createDirectories +import kotlin.io.path.createFile +import kotlin.io.path.createTempDirectory +import kotlin.io.path.deleteRecursively +import kotlin.io.path.exists +import kotlin.io.path.readText +import kotlin.io.path.toPath +import kotlin.io.path.writeText +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.TestInstance + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +abstract class BasePluginTest { + private lateinit var root: Path + lateinit var repo: AppendableMavenFileRepository + + @BeforeEach + open fun setup() { + root = createTempDirectory() + + repo = repo() + repo.module("junit", "junit", "3.8.2") + .use(testJar) + .publish() + + buildScript.writeText( + getProjectBuildScript( + groupInfo = "group = 'shadow'", + versionInfo = "version = '1.0'", + ), + ) + settingsScript.writeText(getSettingsBuildScript()) + } + + @ExperimentalPathApi + @AfterEach + fun cleanup() { + runCatching { + // TODO: workaround for https://github.com/junit-team/junit5/issues/2811. + root.deleteRecursively() + } + + println(buildScript.readText()) + } + + fun getProjectBuildScript( + javaPlugin: String = "java", + groupInfo: String = "", + versionInfo: String = "", + ): String { + return """ + plugins { + id('$javaPlugin') + id('com.gradleup.shadow') + } + $groupInfo + $versionInfo + """.trimIndent() + System.lineSeparator() + } + + fun getSettingsBuildScript( + startBlock: String = "", + endBlock: String = "rootProject.name = 'shadow'", + ): String { + return """ + $startBlock + dependencyResolutionManagement { + repositories { + maven { url = '${repo.uri}' } + mavenCentral() + } + } + $endBlock + """.trimIndent() + System.lineSeparator() + } + + fun publishArtifactA() { + repo.module("shadow", "a", "1.0") + .insertFile("a.properties", "a") + .insertFile("a2.properties", "a2") + .publish() + } + + fun publishArtifactB() { + repo.module("shadow", "b", "1.0") + .insertFile("b.properties", "b") + .publish() + } + + fun publishArtifactCD(circular: Boolean = false) { + repo.module("shadow", "c", "1.0") + .insertFile("c.properties", "c") + .apply { if (circular) dependsOn("d") } + .publish() + repo.module("shadow", "d", "1.0") + .insertFile("d.properties", "d") + .dependsOn("c") + .publish() + } + + open val shadowJarTask = SHADOW_JAR_TASK_NAME + open val runShadowTask = SHADOW_RUN_TASK_NAME + + val buildScript: Path + get() = path("build.gradle") + + val settingsScript: Path + get() = path("settings.gradle") + + val outputShadowJar: Path + get() = path("build/libs/shadow-1.0-all.jar") + + fun path(path: String): Path { + return root.resolve(path).also { + if (!it.exists()) { + it.parent.createDirectories() + it.createFile() + } + } + } + + fun repo(path: String = "maven-repo"): AppendableMavenFileRepository { + return AppendableMavenFileRepository(root.resolve(path)) + } + + fun assertContains(jarPath: Path, paths: List) { + JarFile(jarPath.toFile()).use { jar -> + paths.forEach { path -> + assert(jar.getJarEntry(path) != null) { "Jar file $jarPath does not contain entry $path" } + } + } + } + + fun assertDoesNotContain(jarPath: Path, paths: List) { + JarFile(jarPath.toFile()).use { jar -> + paths.forEach { path -> + assert(jar.getJarEntry(path) == null) { "Jar file $jarPath contains entry $path" } + } + } + } + + private val runner: GradleRunner + get() { + return GradleRunner.create() + .withProjectDir(root.toFile()) + .forwardOutput() + .withPluginClasspath() + .withTestKitDir(testKitDir.toFile()) + } + + fun runner(arguments: Iterable): GradleRunner { + val allArguments = listOf( + "--warning-mode=fail", + "--configuration-cache", + "--stacktrace", + ) + arguments + return runner.withArguments(allArguments) + } + + fun run(vararg tasks: String): BuildResult { + return run(tasks.toList()) + } + + inline fun run( + tasks: Iterable, + block: (GradleRunner) -> GradleRunner = { it }, + ): BuildResult { + return block(runner(tasks)).build().also { + it.assertNoDeprecationWarnings() + } + } + + fun writeClientAndServerModules( + serverShadowBlock: String = "", + ) { + settingsScript.appendText( + """ + include 'client', 'server' + """.trimIndent(), + ) + buildScript.writeText("") + + path("client/src/main/java/client/Client.java").writeText( + """ + package client; + public class Client {} + """.trimIndent(), + ) + path("client/build.gradle").writeText( + """ + ${getProjectBuildScript("java", versionInfo = "version = '1.0'")} + dependencies { implementation 'junit:junit:3.8.2' } + """.trimIndent(), + ) + + path("server/src/main/java/server/Server.java").writeText( + """ + package server; + import client.Client; + public class Server {} + """.trimIndent(), + ) + path("server/build.gradle").writeText( + """ + ${getProjectBuildScript("java", versionInfo = "version = '1.0'")} + dependencies { + implementation project(':client') + } + $shadowJar { + $serverShadowBlock + } + """.trimIndent(), + ) + } + + companion object { + val testKitDir: Path = run { + var gradleUserHome = System.getenv("GRADLE_USER_HOME") + if (gradleUserHome == null) { + gradleUserHome = Path(System.getProperty("user.home"), ".gradle").absolutePathString() + } + Path(gradleUserHome, "testkit") + } + + val testJar: Path = requireNotNull(this::class.java.classLoader.getResource("junit-3.8.2.jar")).toURI().toPath() + + val shadowJar: String = """ + tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name}) + """.trimIndent() + + val runShadow = """ + tasks.named('$SHADOW_RUN_TASK_NAME', ${JavaJarExec::class.java.name}) + """.trimIndent() + + fun BuildResult.assertNoDeprecationWarnings() { + output.lines().forEach { + assert(!containsDeprecationWarning(it)) + } + } + + private fun containsDeprecationWarning(output: String): Boolean { + return output.contains("has been deprecated and is scheduled to be removed in Gradle") || + output.contains("has been deprecated. This is scheduled to be removed in Gradle") + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt new file mode 100644 index 000000000..851994739 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt @@ -0,0 +1,154 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.exists +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import kotlin.io.path.appendText +import kotlin.io.path.deleteExisting +import kotlin.io.path.writeText +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class ConfigurationCacheSpec : BasePluginTest() { + + @BeforeEach + override fun setup() { + super.setup() + publishArtifactA() + publishArtifactB() + buildScript.appendText( + """ + dependencies { + implementation 'shadow:a:1.0' + implementation 'shadow:b:1.0' + } + """.trimIndent() + System.lineSeparator(), + ) + } + + @Test + fun supportsConfigurationCache() { + path("src/main/java/myapp/Main.java").writeText( + """ + package myapp; + public class Main { + public static void main(String[] args) { + System.out.println("TestApp: Hello World! (" + args[0] + ")"); + } + } + """.trimIndent(), + ) + + buildScript.appendText( + """ + apply plugin: 'application' + + application { + mainClass = 'myapp.Main' + } + $runShadow { + args 'foo' + } + """.trimIndent(), + ) + + run(shadowJarTask) + val result = run(shadowJarTask) + + result.assertCcReused() + } + + @Test + fun configurationCachingSupportsExcludes() { + buildScript.appendText( + """ + $shadowJar { + exclude 'a2.properties' + } + """.trimIndent(), + ) + + run(shadowJarTask) + outputShadowJar.deleteExisting() + val result = run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf("a.properties", "b.properties"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("a2.properties"), + ) + result.assertCcReused() + } + + @Test + fun configurationCachingSupportsMinimize() { + writeClientAndServerModules( + serverShadowBlock = """ + minimize { + exclude(dependency('junit:junit:.*')) + } + """.trimIndent(), + ) + val output = path("server/build/libs/server-1.0-all.jar") + + run(shadowJarTask) + output.deleteExisting() + val result = run(shadowJarTask) + + assertThat(output).exists() + assertContains( + output, + listOf("server/Server.class", "junit/framework/Test.class"), + ) + assertDoesNotContain( + output, + listOf("client/Client.class"), + ) + result.assertCcReused() + } + + @Test + fun configurationCachingOfConfigurationsIsUpToDate() { + settingsScript.appendText( + """ + include 'lib' + """.trimIndent(), + ) + + path("lib/src/main/java/lib/Lib.java").writeText( + """ + package lib; + public class Lib {} + """.trimIndent(), + ) + path("lib/build.gradle").writeText( + """ + ${getProjectBuildScript()} + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + configurations = [project.configurations.compileClasspath] + } + """.trimIndent(), + ) + + run(shadowJarTask) + val result = run(shadowJarTask) + + assertThat(result.task(":lib:shadowJar")).isNotNull() + .transform { it.outcome }.isEqualTo(TaskOutcome.UP_TO_DATE) + result.assertCcReused() + } + + private fun BuildResult.assertCcReused() { + assertThat(output).contains("Reusing configuration cache.") + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt new file mode 100644 index 000000000..a639f7b11 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -0,0 +1,288 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertThat +import assertk.assertions.exists +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import kotlin.io.path.appendText +import kotlin.io.path.readText +import kotlin.io.path.writeText +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class FilteringTest : BasePluginTest() { + + @BeforeEach + override fun setup() { + super.setup() + publishArtifactA() + publishArtifactB() + + buildScript.appendText( + """ + dependencies { + implementation 'shadow:a:1.0' + implementation 'shadow:b:1.0' + } + """.trimIndent() + System.lineSeparator(), + ) + } + + @Test + fun includeAllDependencies() { + run(shadowJarTask) + assertContains( + outputShadowJar, + listOf("a.properties", "a2.properties", "b.properties"), + ) + } + + @Test + fun excludeFiles() { + buildScript.appendText( + """ + $shadowJar { + exclude 'a2.properties' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf("a.properties", "b.properties"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("a2.properties"), + ) + } + + @Test + fun excludeDependency() { + publishArtifactCD() + dependOnAndExcludeArtifactD() + + run(shadowJarTask) + + commonAssertions() + } + + @Test + fun excludeDependencyUsingWildcardSyntax() { + publishArtifactCD() + buildScript.appendText( + """ + dependencies { + implementation 'shadow:d:1.0' + } + $shadowJar { + dependencies { + exclude(dependency('shadow:d:.*')) + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + commonAssertions() + } + + @Test + fun dependencyExclusionsAffectUpToDateCheck() { + publishArtifactCD() + dependOnAndExcludeArtifactD() + + run(shadowJarTask) + + commonAssertions() + + val replaced = buildScript.readText() + .replace("exclude(dependency('shadow:d:1.0'))", "exclude(dependency('shadow:c:1.0'))") + buildScript.writeText(replaced) + val result = run(shadowJarTask) + + assertThat(result.task(":shadowJar")).isNotNull() + .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) + assertContains( + outputShadowJar, + listOf("a.properties", "a2.properties", "b.properties", "d.properties"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("c.properties"), + ) + } + + @Test + fun projectExclusionsAffectUpToDateCheck() { + publishArtifactCD() + dependOnAndExcludeArtifactD() + + run(shadowJarTask) + + commonAssertions() + + val replaced = buildScript.readText() + .replace("exclude(dependency('shadow:d:1.0'))", "exclude 'a.properties'") + buildScript.writeText(replaced) + + val result = run(shadowJarTask) + + assertThat(result.task(":shadowJar")).isNotNull() + .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) + assertContains( + outputShadowJar, + listOf("a2.properties", "b.properties", "c.properties", "d.properties"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("a.properties"), + ) + } + + @Test + fun includeDependencyAndExcludeOthers() { + publishArtifactCD() + buildScript.appendText( + """ + dependencies { + implementation 'shadow:d:1.0' + } + $shadowJar { + dependencies { + include(dependency('shadow:d:1.0')) + } + } + """.trimIndent(), + ) + path("src/main/java/shadow/Passed.java").writeText( + """ + package shadow; + public class Passed {} + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf("d.properties", "shadow/Passed.class"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("a.properties", "a2.properties", "b.properties", "c.properties"), + ) + } + + @Test + fun filterProjectDependencies() { + writeClientAndServerModules( + serverShadowBlock = """ + dependencies { + exclude(project(':client')) + } + """.trimIndent(), + ) + + val serverOutput = path("server/build/libs/server-1.0-all.jar") + run(":server:$shadowJarTask") + + assertThat(serverOutput).exists() + assertDoesNotContain( + serverOutput, + listOf("client/Client.class"), + ) + assertContains( + serverOutput, + listOf("server/Server.class", "junit/framework/Test.class"), + ) + } + + @Test + fun excludeATransitiveProjectDependency() { + writeClientAndServerModules( + serverShadowBlock = """ + dependencies { + exclude(dependency { it.moduleGroup == 'junit' }) + } + """.trimIndent(), + ) + + val serverOutput = path("server/build/libs/server-1.0-all.jar") + run(":server:$shadowJarTask") + + assertThat(serverOutput).exists() + assertDoesNotContain( + serverOutput, + listOf("junit/framework/Test.class"), + ) + assertContains( + serverOutput, + listOf("client/Client.class", "server/Server.class"), + ) + } + + @Test + fun verifyExcludePrecedenceOverInclude() { + buildScript.appendText( + """ + $shadowJar { + include '*.jar' + include '*.properties' + exclude 'a2.properties' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf("a.properties", "b.properties"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("a2.properties"), + ) + } + + @Test + fun handleExcludeWithCircularDependency() { + publishArtifactCD(circular = true) + dependOnAndExcludeArtifactD() + + run(shadowJarTask) + + commonAssertions() + } + + private fun dependOnAndExcludeArtifactD() { + buildScript.appendText( + """ + dependencies { + implementation 'shadow:d:1.0' + } + $shadowJar { + dependencies { + exclude(dependency('shadow:d:1.0')) + } + } + """.trimIndent(), + ) + } + + private fun commonAssertions() { + assertContains( + outputShadowJar, + listOf("a.properties", "a2.properties", "b.properties", "c.properties"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("d.properties"), + ) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt new file mode 100644 index 000000000..e502abfe4 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -0,0 +1,390 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.exists +import assertk.assertions.isEqualTo +import assertk.assertions.isInstanceOf +import java.net.URLClassLoader +import java.util.jar.JarFile +import kotlin.io.path.appendText +import kotlin.io.path.writeText +import org.junit.jupiter.api.Test + +class RelocationTest : BasePluginTest() { + + @Test + fun defaultEnableRelocation() { + buildScript.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + enableRelocation = true + } + """.trimIndent(), + ) + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf( + "META-INF/MANIFEST.MF", + "shadow/junit/textui/ResultPrinter.class", + "shadow/junit/textui/TestRunner.class", + "shadow/junit/framework/Assert.class", + "shadow/junit/framework/AssertionFailedError.class", + "shadow/junit/framework/ComparisonCompactor.class", + "shadow/junit/framework/ComparisonFailure.class", + "shadow/junit/framework/Protectable.class", + "shadow/junit/framework/Test.class", + "shadow/junit/framework/TestCase.class", + "shadow/junit/framework/TestFailure.class", + "shadow/junit/framework/TestListener.class", + "shadow/junit/framework/TestResult$1.class", + "shadow/junit/framework/TestResult.class", + "shadow/junit/framework/TestSuite$1.class", + "shadow/junit/framework/TestSuite.class", + ), + ) + } + + /** + * https://github.com/GradleUp/shadow/issues/58 + */ + @Test + fun relocateDependencyFiles() { + buildScript.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + relocate 'junit.textui', 'a' + relocate 'junit.framework', 'b' + manifest { + attributes 'TEST-VALUE': 'FOO' + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf( + "META-INF/MANIFEST.MF", + "a/ResultPrinter.class", + "a/TestRunner.class", + "b/Assert.class", + "b/AssertionFailedError.class", + "b/ComparisonCompactor.class", + "b/ComparisonFailure.class", + "b/Protectable.class", + "b/Test.class", + "b/TestCase.class", + "b/TestFailure.class", + "b/TestListener.class", + "b/TestResult\$1.class", + "b/TestResult.class", + "b/TestSuite\$1.class", + "b/TestSuite.class", + ), + ) + + assertDoesNotContain( + outputShadowJar, + listOf( + "junit/textui/ResultPrinter.class", + "junit/textui/TestRunner.class", + "junit/framework/Assert.class", + "junit/framework/AssertionFailedError.class", + "junit/framework/ComparisonCompactor.class", + "junit/framework/ComparisonFailure.class", + "junit/framework/Protectable.class", + "junit/framework/Test.class", + "junit/framework/TestCase.class", + "junit/framework/TestFailure.class", + "junit/framework/TestListener.class", + "junit/framework/TestResult\$1.class", + "junit/framework/TestResult.class", + "junit/framework/TestSuite\$1.class", + "junit/framework/TestSuite.class", + ), + ) + + val jarFile = JarFile(outputShadowJar.toFile()) + assertThat(jarFile.manifest.mainAttributes.getValue("TEST-VALUE")).isEqualTo("FOO") + } + + @Test + fun relocateDependencyFilesWithFiltering() { + buildScript.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + relocate('junit.textui', 'a') { + exclude 'junit.textui.TestRunner' + } + relocate('junit.framework', 'b') { + include 'junit.framework.Test*' + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf( + "a/ResultPrinter.class", + "b/Test.class", + "b/TestCase.class", + "b/TestFailure.class", + "b/TestListener.class", + "b/TestResult\$1.class", + "b/TestResult.class", + "b/TestSuite\$1.class", + "b/TestSuite.class", + ), + ) + + assertDoesNotContain( + outputShadowJar, + listOf( + "a/TestRunner.class", + "b/Assert.class", + "b/AssertionFailedError.class", + "b/ComparisonCompactor.class", + "b/ComparisonFailure.class", + "b/Protectable.class", + ), + ) + + assertContains( + outputShadowJar, + listOf( + "junit/textui/TestRunner.class", + "junit/framework/Assert.class", + "junit/framework/AssertionFailedError.class", + "junit/framework/ComparisonCompactor.class", + "junit/framework/ComparisonFailure.class", + "junit/framework/Protectable.class", + ), + ) + } + + /** + * https://github.com/GradleUp/shadow/issues/53 + * https://github.com/GradleUp/shadow/issues/55 + */ + @Test + fun remapClassNamesForRelocatedFilesInProjectSource() { + buildScript.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + relocate 'junit.framework', 'shadow.junit' + } + """.trimIndent(), + ) + + path("src/main/java/shadow/ShadowTest.java").writeText( + """ + package shadow; + + import junit.framework.Test; + import junit.framework.TestResult; + + public class ShadowTest implements Test { + public int countTestCases() { return 0; } + public void run(TestResult result) { } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf( + "shadow/ShadowTest.class", + "shadow/junit/Test.class", + "shadow/junit", + ), + ) + + assertDoesNotContain( + outputShadowJar, + listOf( + "junit/framework", + "junit/framework/Test.class", + ), + ) + + val classLoader = URLClassLoader( + arrayOf(outputShadowJar.toUri().toURL()), + ClassLoader.getSystemClassLoader().parent, + ) + assertFailure { + // check that the class can be loaded. If the file was not relocated properly, we should get a NoDefClassFound + // Isolated class loader with only the JVM system jars and the output jar from the test project + classLoader.loadClass("shadow.ShadowTest") + error("Should not reach here.") + }.isInstanceOf(IllegalStateException::class) + } + + @Test + fun relocateDoesNotDropDependencyResources() { + path("core/build.gradle").writeText( + """ + plugins { + id 'java-library' + } + dependencies { + api 'junit:junit:3.8.2' + } + """.trimIndent(), + ) + + path("core/src/main/resources/TEST").writeText("TEST RESOURCE") + path("core/src/main/resources/test.properties").writeText("name=test") + path("core/src/main/java/core/Core.java").writeText( + """ + package core; + + import junit.framework.Test; + + public class Core {} + """.trimIndent(), + ) + + path("app/build.gradle").writeText( + """ + ${getProjectBuildScript()} + dependencies { + implementation project(':core') + } + $shadowJar { + relocate 'core', 'app.core' + relocate 'junit.framework', 'app.junit.framework' + } + """.trimIndent(), + ) + path("app/src/main/resources/APP-TEST").writeText("APP TEST RESOURCE") + path("app/src/main/java/app/App.java").writeText( + """ + package app; + + import core.Core; + import junit.framework.Test; + + public class App {} + """.trimIndent(), + ) + + settingsScript.appendText( + """ + include 'core', 'app' + """.trimIndent(), + ) + + run(":app:$shadowJarTask") + + val appOutput = path("app/build/libs/app-all.jar") + assertThat(appOutput).exists() + + assertContains( + appOutput, + listOf( + "TEST", + "APP-TEST", + "test.properties", + "app/core/Core.class", + "app/App.class", + "app/junit/framework/Test.class", + ), + ) + } + + /** + * https://github.com/GradleUp/shadow/issues/93 + * https://github.com/GradleUp/shadow/issues/114 + */ + @Test + fun relocateResourceFiles() { + repo.module("shadow", "dep", "1.0") + .insertFile("foo/dep.properties", "c") + .publish() + path("src/main/java/foo/Foo.java").writeText( + """ + package foo; + + class Foo {} + """.trimIndent(), + ) + path("src/main/resources/foo/foo.properties").writeText("name=foo") + + buildScript.appendText( + """ + dependencies { + implementation 'shadow:dep:1.0' + } + $shadowJar { + relocate 'foo', 'bar' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf( + "bar/Foo.class", + "bar/foo.properties", + "bar/dep.properties", + ), + ) + assertDoesNotContain( + outputShadowJar, + listOf( + "foo/Foo.class", + "foo/foo.properties", + "foo/dep.properties", + ), + ) + } + + /** + * https://github.com/GradleUp/shadow/issues/294 + */ + @Test + fun doesNotErrorOnRelocatingJava9Classes() { + buildScript.appendText( + """ + dependencies { + implementation 'org.slf4j:slf4j-api:1.7.21' + implementation group: 'io.netty', name: 'netty-all', version: '4.0.23.Final' + implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '2.5.0' + implementation group: 'org.apache.zookeeper', name: 'zookeeper', version: '3.4.6' + } + $shadowJar { + zip64 = true + relocate 'com.google.protobuf', 'shaded.com.google.protobuf' + relocate 'io.netty', 'shaded.io.netty' + } + """.trimIndent(), + ) + + run(shadowJarTask) + // No exception should be thrown + } +} diff --git a/src/intiTest/resources/junit-3.8.2.jar b/src/intiTest/resources/junit-3.8.2.jar new file mode 100644 index 0000000000000000000000000000000000000000..c8f711d050eff209321f799d85ebb3bbe305d481 GIT binary patch literal 120640 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 A>i_@% literal 0 HcmV?d00001 diff --git a/src/intiTest/resources/test-artifact-1.0-SNAPSHOT.jar b/src/intiTest/resources/test-artifact-1.0-SNAPSHOT.jar new file mode 100644 index 0000000000000000000000000000000000000000..009abad49de5cbf4857e8c429cb1f81c75c2dd56 GIT binary patch literal 3115 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 ZiPmb76=Ewli&KF45oX>c2|m`p{{iiGhC2WN literal 0 HcmV?d00001 From 0a9f0c8bcdd401d1187d1d75b254b1a127f6d757 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 29 Dec 2024 03:41:26 -0500 Subject: [PATCH 117/941] Align logic for functional test kits (#1122) * Rename contains and doesNotContain * Binary files should be asserted to exist, text files should be created * Remove unused assertJarFileContentsEqual * Replace runWithDebug with run * Cleanups * Rename getOutput to getOutputShadowJar * Rename getDefaultBuildScript to getProjectBuildScript * Rename dir to root * Rename getBuildFile and getSettingsFile * Fix * Merge getFile logic into file * Align getProjectBuildScript logic * Don't assert existing in path * Cleanup * Rename getProjectBuildScript and getDefaultSettingsBuildScript * Rename getBuildScript and getSettingsScript --- .../shadow/BasePluginSpecification.groovy | 76 ++++---- .../plugins/shadow/PublishingSpec.groovy | 24 +-- .../plugins/shadow/ShadowPluginSpec.groovy | 176 +++++++++--------- .../plugins/shadow/TransformerSpec.groovy | 114 ++++++------ .../shadow/caching/AbstractCachingSpec.groovy | 12 +- .../caching/MinimizationCachingSpec.groovy | 26 +-- .../caching/RelocationCachingSpec.groovy | 18 +- .../caching/ShadowJarCachingSpec.groovy | 52 +++--- .../caching/TransformCachingSpec.groovy | 82 ++++---- .../gradle/plugins/shadow/ApplicationTest.kt | 6 +- .../gradle/plugins/shadow/BasePluginTest.kt | 44 ++--- .../plugins/shadow/ConfigurationCacheSpec.kt | 12 +- .../gradle/plugins/shadow/FilteringTest.kt | 24 +-- .../gradle/plugins/shadow/RelocationTest.kt | 16 +- 14 files changed, 336 insertions(+), 346 deletions(-) diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy index 6b377d860..8ac09974d 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.AppendableJar import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository +import org.apache.commons.lang3.StringUtils import org.codehaus.plexus.util.IOUtil import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner @@ -18,25 +19,25 @@ import java.util.jar.JarFile abstract class BasePluginSpecification extends Specification { @TempDir - Path dir + Path root AppendableMavenFileRepository repo def setup() { repo = repo() - repo.module('junit', 'junit', '3.8.2').use(testJar).publish() + repo.module('junit', 'junit', '3.8.2') + .use(Paths.get(this.class.classLoader.getResource('junit-3.8.2.jar').toURI())) + .publish() - buildFile << getDefaultBuildScript('java', true, true) - buildFile << System.lineSeparator() - settingsFile << settingsBuildScript - settingsFile << System.lineSeparator() + projectScriptFile << getDefaultProjectBuildScript('java', true, true) + settingsScriptFile << getDefaultSettingsBuildScript() } def cleanup() { - println buildFile.text + println projectScriptFile.text } - String getDefaultBuildScript( + String getDefaultProjectBuildScript( String javaPlugin = 'java', boolean withGroup = false, boolean withVersion = false @@ -52,10 +53,10 @@ abstract class BasePluginSpecification extends Specification { $groupInfo $versionInfo - """.stripIndent().trim() + """.stripIndent().trim() + System.lineSeparator() } - String getSettingsBuildScript(boolean withRootProject = true) { + String getDefaultSettingsBuildScript(boolean withRootProject = true) { def rootProjectInfo = withRootProject ? "rootProject.name = 'shadow'" : "" return """ dependencyResolutionManagement { @@ -66,14 +67,14 @@ abstract class BasePluginSpecification extends Specification { } $rootProjectInfo - """.stripIndent().trim() + """.stripIndent().trim() + System.lineSeparator() } static def shadowJar = "tasks.named('shadowJar', ${ShadowJar.class.name})".trim() GradleRunner getRunner() { GradleRunner.create() - .withProjectDir(dir.toFile()) + .withProjectDir(root.toFile()) .forwardOutput() .withPluginClasspath() .withTestKitDir(testKitDir) @@ -93,37 +94,40 @@ abstract class BasePluginSpecification extends Specification { return result } - BuildResult runWithDebug(String... tasks) { - return run(tasks.toList(), { it.withDebug(true) }) - } - BuildResult runWithFailure(List tasks, Function runnerFunction = { it }) { def result = runnerFunction.apply(runner(tasks)).buildAndFail() assertNoDeprecationWarnings(result) return result } - static void assertNoDeprecationWarnings(BuildResult result) { + private static void assertNoDeprecationWarnings(BuildResult result) { result.output.eachLine { assert !containsDeprecationWarning(it) } } - static boolean containsDeprecationWarning(String output) { + private static boolean containsDeprecationWarning(String output) { output.contains("has been deprecated and is scheduled to be removed in Gradle") || output.contains("has been deprecated. This is scheduled to be removed in Gradle") } - File getBuildFile() { + File getProjectScriptFile() { file('build.gradle') } - File getSettingsFile() { + File getSettingsScriptFile() { file('settings.gradle') } File file(String path) { - File f = dir.resolve(path).toFile() + File f = root.resolve(path).toFile() + String extension = StringUtils.substringAfterLast(path, '.') + + // Binary files should be asserted to exist, text files should be created. + if (extension == "jar" || extension == "zip") { + return f + } + if (!f.exists()) { f.parentFile.mkdirs() if (!f.createNewFile()) { @@ -133,16 +137,8 @@ abstract class BasePluginSpecification extends Specification { return f } - File getFile(String path) { - return dir.resolve(path).toFile() - } - AppendableMavenFileRepository repo(String path = 'maven-repo') { - new AppendableMavenFileRepository(dir.resolve(path)) - } - - void assertJarFileContentsEqual(File f, String path, String contents) { - assert getJarFileContents(f, path) == contents + new AppendableMavenFileRepository(root.resolve(path)) } String getJarFileContents(File f, String path) { @@ -155,7 +151,7 @@ abstract class BasePluginSpecification extends Specification { return sw.toString() } - void contains(File f, List paths) { + void assertContains(File f, List paths) { JarFile jar = new JarFile(f) paths.each { path -> assert jar.getJarEntry(path), "${f.path} does not contain [$path]" @@ -163,7 +159,7 @@ abstract class BasePluginSpecification extends Specification { jar.close() } - void doesNotContain(File f, List paths) { + void assertDoesNotContain(File f, List paths) { JarFile jar = new JarFile(f) paths.each { path -> assert !jar.getJarEntry(path), "${f.path} contains [$path]" @@ -184,19 +180,11 @@ abstract class BasePluginSpecification extends Specification { return new AppendableJar(file(path).toPath()) } - protected File getOutput() { - getFile('build/libs/shadow-1.0-all.jar') - } - - protected File output(String name) { - getFile("build/libs/${name}") - } - - protected Path getTestJar(String name = 'junit-3.8.2.jar') { - return Paths.get(this.class.classLoader.getResource(name).toURI()) + File getOutputShadowJar() { + file('build/libs/shadow-1.0-all.jar') } - protected static File getTestKitDir() { + static File getTestKitDir() { def gradleUserHome = System.getenv("GRADLE_USER_HOME") if (!gradleUserHome) { gradleUserHome = new File(System.getProperty("user.home"), ".gradle").absolutePath @@ -204,7 +192,7 @@ abstract class BasePluginSpecification extends Specification { return new File(gradleUserHome, "testkit") } - protected static String escapedPath(Path path) { + static String escapedPath(Path path) { return path.toString().replaceAll('\\\\', '\\\\\\\\') } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy index 9b95a42f0..08e9b8170 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy @@ -26,8 +26,8 @@ class PublishingSpec extends BasePluginSpecification { .insertFile('b.properties', 'b') .publish() - settingsFile << "rootProject.name = 'maven'" - buildFile << """ + settingsScriptFile << "rootProject.name = 'maven'" + projectScriptFile << """ apply plugin: 'maven-publish' dependencies { @@ -63,7 +63,7 @@ class PublishingSpec extends BasePluginSpecification { assert publishedFile.exists() and: - contains(publishedFile, ['a.properties', 'a2.properties']) + assertContains(publishedFile, ['a.properties', 'a2.properties']) and: File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile @@ -93,8 +93,8 @@ class PublishingSpec extends BasePluginSpecification { .insertFile('b.properties', 'b') .publish() - settingsFile << "rootProject.name = 'maven'" - buildFile << """ + settingsScriptFile << "rootProject.name = 'maven'" + projectScriptFile << """ apply plugin: 'maven-publish' dependencies { @@ -134,14 +134,14 @@ class PublishingSpec extends BasePluginSpecification { def "publish multiproject shadow jar with maven-publish plugin"() { given: - settingsFile << """ + settingsScriptFile << """ rootProject.name = 'maven' include 'a' include 'b' include 'c' """.stripMargin() - buildFile.text = """ + projectScriptFile.text = """ subprojects { apply plugin: 'java' apply plugin: 'maven-publish' @@ -211,7 +211,7 @@ class PublishingSpec extends BasePluginSpecification { assert publishedFile.exists() and: - contains(publishedFile, ['a.properties', 'a2.properties']) + assertContains(publishedFile, ['a.properties', 'a2.properties']) and: File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile @@ -237,10 +237,10 @@ class PublishingSpec extends BasePluginSpecification { .insertFile('b.properties', 'b') .publish() - settingsFile << """ + settingsScriptFile << """ rootProject.name = 'maven' """ - buildFile << """ + projectScriptFile << """ apply plugin: 'maven-publish' dependencies { implementation 'shadow:a:1.0' @@ -277,7 +277,7 @@ class PublishingSpec extends BasePluginSpecification { assert shadowJar.exists() and: - contains(shadowJar, ['a.properties', 'a2.properties']) + assertContains(shadowJar, ['a.properties', 'a2.properties']) and: "publishes both a POM file and a Gradle metadata file" File pom = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.pom').toFile().canonicalFile @@ -328,7 +328,7 @@ class PublishingSpec extends BasePluginSpecification { assertions { shadowJar = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0-all.jar').toFile().canonicalFile assert shadowJar.exists() - contains(shadowJar, ['a.properties', 'a2.properties']) + assertContains(shadowJar, ['a.properties', 'a2.properties']) } assertions { diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy index 139c6d356..5117778e4 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy @@ -71,7 +71,7 @@ class ShadowPluginSpec extends BasePluginSpecification { repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', 'two # NOTE: No newline terminates this line/file').publish() - buildFile << """ + projectScriptFile << """ dependencies { implementation 'junit:junit:3.8.2' implementation files('${escapedPath(one)}') @@ -88,7 +88,7 @@ class ShadowPluginSpec extends BasePluginSpecification { } then: - assert output.exists() + assert outputShadowJar.exists() where: version << ['8.3'] @@ -96,7 +96,7 @@ class ShadowPluginSpec extends BasePluginSpecification { def 'Error in Gradle versions < 8.3'() { given: - buildFile << """ + projectScriptFile << """ dependencies { implementation 'junit:junit:3.8.2' } @@ -117,7 +117,7 @@ class ShadowPluginSpec extends BasePluginSpecification { URL artifact = this.class.classLoader.getResource('test-artifact-1.0-SNAPSHOT.jar') URL project = this.class.classLoader.getResource('test-project-1.0-SNAPSHOT.jar') - buildFile << """ + projectScriptFile << """ $shadowJar { from('${artifact.path}') from('${project.path}') @@ -128,7 +128,7 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() } def 'include project sources'() { @@ -138,7 +138,7 @@ class ShadowPluginSpec extends BasePluginSpecification { public class Passed {} '''.stripIndent() - buildFile << """ + projectScriptFile << """ dependencies { implementation 'junit:junit:3.8.2' } $shadowJar { @@ -153,10 +153,10 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - contains(output("shadow.jar"), ['shadow/Passed.class', 'junit/framework/Test.class']) + assertContains(file("build/libs/shadow.jar"), ['shadow/Passed.class', 'junit/framework/Test.class']) and: - doesNotContain(output("shadow.jar"), ['/']) + assertDoesNotContain(file("build/libs/shadow.jar"), ['/']) } def 'include project dependencies'() { @@ -185,20 +185,20 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript dependencies { implementation project(':client') } """.stripIndent() - File serverOutput = getFile('server/build/libs/server-all.jar') + File serverOutput = file('server/build/libs/server-all.jar') when: run(':server:shadowJar') then: serverOutput.exists() - contains(serverOutput, [ + assertContains(serverOutput, [ 'client/Client.class', 'server/Server.class', 'junit/framework/Test.class' @@ -237,7 +237,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript $shadowJar { minimize() @@ -246,18 +246,18 @@ class ShadowPluginSpec extends BasePluginSpecification { dependencies { implementation project(':client') } """.stripIndent() - File serverOutput = getFile('server/build/libs/server-all.jar') + File serverOutput = file('server/build/libs/server-all.jar') when: - runWithDebug(':server:shadowJar') + run(':server:shadowJar') then: serverOutput.exists() - contains(serverOutput, [ + assertContains(serverOutput, [ 'client/Client.class', 'server/Server.class' ]) - doesNotContain(serverOutput, ['junit/framework/Test.class']) + assertDoesNotContain(serverOutput, ['junit/framework/Test.class']) } /** @@ -288,7 +288,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript $shadowJar { minimize { @@ -299,18 +299,18 @@ class ShadowPluginSpec extends BasePluginSpecification { dependencies { implementation project(':client') } """.stripIndent() - File serverOutput = getFile('server/build/libs/server-all.jar') + File serverOutput = file('server/build/libs/server-all.jar') when: - runWithDebug(':server:shadowJar') + run(':server:shadowJar') then: serverOutput.exists() - contains(serverOutput, [ + assertContains(serverOutput, [ 'server/Server.class', 'junit/framework/Test.class' ]) - doesNotContain(serverOutput, ['client/Client.class']) + assertDoesNotContain(serverOutput, ['client/Client.class']) } /** @@ -339,7 +339,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript $shadowJar { minimize { @@ -353,10 +353,10 @@ class ShadowPluginSpec extends BasePluginSpecification { File serverOutput = file('server/build/libs/server-all.jar') when: - runWithDebug(':server:shadowJar') + run(':server:shadowJar') then: - contains(serverOutput, [ + assertContains(serverOutput, [ 'client/Client.class', 'server/Server.class' ]) @@ -392,7 +392,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript $shadowJar { minimize { @@ -406,10 +406,10 @@ class ShadowPluginSpec extends BasePluginSpecification { File serverOutput = file('server/build/libs/server-all.jar') when: - runWithDebug(':server:shadowJar') + run(':server:shadowJar') then: - contains(serverOutput, [ + assertContains(serverOutput, [ 'client/Client.class', 'server/Server.class', 'junit/framework/TestCase.class' @@ -443,7 +443,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript $shadowJar { minimize { @@ -457,10 +457,10 @@ class ShadowPluginSpec extends BasePluginSpecification { File serverOutput = file('server/build/libs/server-all.jar') when: - runWithDebug(':server:shadowJar') + run(':server:shadowJar') then: - contains(serverOutput, [ + assertContains(serverOutput, [ 'client/Client.class', 'server/Server.class', 'junit/framework/TestCase.class' @@ -521,7 +521,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('impl/build.gradle') << """ - ${getDefaultBuildScript('java-library')} + ${getDefaultProjectBuildScript('java-library')} $shadowJar { minimize() @@ -530,20 +530,20 @@ class ShadowPluginSpec extends BasePluginSpecification { dependencies { api project(':api') } """.stripIndent() - File serverOutput = getFile('impl/build/libs/impl-all.jar') + File serverOutput = file('impl/build/libs/impl-all.jar') when: - runWithDebug(':impl:shadowJar') + run(':impl:shadowJar') then: serverOutput.exists() - contains(serverOutput, [ + assertContains(serverOutput, [ 'impl/SimpleEntity.class', 'api/Entity.class', 'api/UnusedEntity.class', 'lib/LibEntity.class', ]) - doesNotContain(serverOutput, ['junit/framework/Test.class', 'lib/UnusedLibEntity.class']) + assertDoesNotContain(serverOutput, ['junit/framework/Test.class', 'lib/UnusedLibEntity.class']) } /** @@ -595,7 +595,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('impl/build.gradle') << """ - ${getDefaultBuildScript('java-library')} + ${getDefaultProjectBuildScript('java-library')} $shadowJar { minimize() @@ -604,14 +604,14 @@ class ShadowPluginSpec extends BasePluginSpecification { dependencies { api project(':api') } """.stripIndent() - File serverOutput = getFile('impl/build/libs/impl-all.jar') + File serverOutput = file('impl/build/libs/impl-all.jar') when: - runWithDebug(':impl:shadowJar') + run(':impl:shadowJar') then: serverOutput.exists() - contains(serverOutput, [ + assertContains(serverOutput, [ 'impl/SimpleEntity.class', 'api/Entity.class', 'api/UnusedEntity.class', @@ -632,7 +632,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('client/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript dependencies { implementation 'junit:junit:3.8.2' } @@ -656,19 +656,19 @@ class ShadowPluginSpec extends BasePluginSpecification { dependencies { implementation project(path: ':client', configuration: 'shadow') } """.stripIndent() - File serverOutput = getFile('server/build/libs/server.jar') + File serverOutput = file('server/build/libs/server.jar') when: run(':server:jar') then: serverOutput.exists() - contains(serverOutput, [ + assertContains(serverOutput, [ 'server/Server.class' ]) and: - doesNotContain(serverOutput, [ + assertDoesNotContain(serverOutput, [ 'client/Client.class', 'junit/framework/Test.class', 'client/junit/framework/Test.class' @@ -687,7 +687,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('client/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript dependencies { implementation 'junit:junit:3.8.2' } @@ -706,26 +706,26 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('server/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript dependencies { implementation project(path: ':client', configuration: 'shadow') } """.stripIndent() - File serverOutput = getFile('server/build/libs/server-all.jar') + File serverOutput = file('server/build/libs/server-all.jar') when: run(':server:shadowJar') then: serverOutput.exists() - contains(serverOutput, [ + assertContains(serverOutput, [ 'client/Client.class', 'client/junit/framework/Test.class', 'server/Server.class', ]) and: - doesNotContain(serverOutput, [ + assertDoesNotContain(serverOutput, [ 'junit/framework/Test.class' ]) } @@ -746,7 +746,7 @@ class ShadowPluginSpec extends BasePluginSpecification { public class Passed {} '''.stripIndent() - buildFile << """ + projectScriptFile << """ dependencies { implementation 'shadow:a:1.0' } """.stripIndent() @@ -754,10 +754,10 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - contains(output, ['a.properties', 'META-INF/a.properties']) + assertContains(outputShadowJar, ['a.properties', 'META-INF/a.properties']) and: - doesNotContain(output, ['META-INF/INDEX.LIST', 'META-INF/a.SF', 'META-INF/a.DSA', 'META-INF/a.RSA']) + assertDoesNotContain(outputShadowJar, ['META-INF/INDEX.LIST', 'META-INF/a.SF', 'META-INF/a.DSA', 'META-INF/a.RSA']) } def "include runtime configuration by default"() { @@ -770,7 +770,7 @@ class ShadowPluginSpec extends BasePluginSpecification { .insertFile('b.properties', 'b') .publish() - buildFile << """ + projectScriptFile << """ dependencies { runtimeOnly 'shadow:a:1.0' shadow 'shadow:b:1.0' @@ -781,10 +781,10 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - contains(output, ['a.properties']) + assertContains(outputShadowJar, ['a.properties']) and: - doesNotContain(output, ['b.properties']) + assertDoesNotContain(outputShadowJar, ['b.properties']) } def "include java-library configurations by default"() { @@ -806,8 +806,8 @@ class ShadowPluginSpec extends BasePluginSpecification { .insertFile('runtimeOnly.properties', 'runtimeOnly') .publish() - buildFile.text = getDefaultBuildScript('java-library', true, true) - buildFile << """ + projectScriptFile.text = getDefaultProjectBuildScript('java-library', true, true) + projectScriptFile << """ dependencies { api 'shadow:api:1.0' implementation 'shadow:implementation:1.0' @@ -816,11 +816,11 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() when: - runWithDebug('shadowJar') + run('shadowJar') then: - contains(output, ['api.properties', 'implementation.properties', - 'runtimeOnly.properties', 'implementation-dep.properties']) + assertContains(outputShadowJar, ['api.properties', 'implementation.properties', + 'runtimeOnly.properties', 'implementation-dep.properties']) } def "doesn't include compileOnly configuration by default"() { @@ -833,7 +833,7 @@ class ShadowPluginSpec extends BasePluginSpecification { .insertFile('b.properties', 'b') .publish() - buildFile << """ + projectScriptFile << """ dependencies { runtimeOnly 'shadow:a:1.0' compileOnly 'shadow:b:1.0' @@ -844,10 +844,10 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - contains(output, ['a.properties']) + assertContains(outputShadowJar, ['a.properties']) and: - doesNotContain(output, ['b.properties']) + assertDoesNotContain(outputShadowJar, ['b.properties']) } def "default copying strategy"() { @@ -860,7 +860,7 @@ class ShadowPluginSpec extends BasePluginSpecification { .insertFile('META-INF/MANIFEST.MF', 'MANIFEST B') .publish() - buildFile << """ + projectScriptFile << """ dependencies { runtimeOnly 'shadow:a:1.0' runtimeOnly 'shadow:b:1.0' @@ -871,14 +871,14 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - JarFile jar = new JarFile(output) + JarFile jar = new JarFile(outputShadowJar) assert jar.entries().collect().size() == 2 } def "Class-Path in Manifest not added if empty"() { given: - buildFile << """ + projectScriptFile << """ dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -886,10 +886,10 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - JarFile jar = new JarFile(output) + JarFile jar = new JarFile(outputShadowJar) Attributes attributes = jar.manifest.getMainAttributes() assert attributes.getValue('Class-Path') == null } @@ -898,7 +898,7 @@ class ShadowPluginSpec extends BasePluginSpecification { def "add shadow configuration to Class-Path in Manifest"() { given: - buildFile << """ + projectScriptFile << """ dependencies { shadow 'junit:junit:3.8.2' } @@ -914,10 +914,10 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: 'https://github.com/GradleUp/shadow/issues/65 - combine w/ existing Class-Path' - JarFile jar = new JarFile(output) + JarFile jar = new JarFile(outputShadowJar) Attributes attributes = jar.manifest.getMainAttributes() String classpath = attributes.getValue('Class-Path') assert classpath == '/libs/a.jar junit-3.8.2.jar' @@ -928,7 +928,7 @@ class ShadowPluginSpec extends BasePluginSpecification { def "do not include null value in Class-Path when jar file does not contain Class-Path"() { given: - buildFile << """ + projectScriptFile << """ dependencies { shadow 'junit:junit:3.8.2' } """.stripIndent() @@ -936,10 +936,10 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - JarFile jar = new JarFile(output) + JarFile jar = new JarFile(outputShadowJar) Attributes attributes = jar.manifest.getMainAttributes() String classpath = attributes.getValue('Class-Path') assert classpath == 'junit-3.8.2.jar' @@ -950,7 +950,7 @@ class ShadowPluginSpec extends BasePluginSpecification { def "support ZipCompression.STORED"() { given: - buildFile << """ + projectScriptFile << """ dependencies { shadow 'junit:junit:3.8.2' } $shadowJar { @@ -963,7 +963,7 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() } @@ -995,7 +995,7 @@ class ShadowPluginSpec extends BasePluginSpecification { """.stripIndent() file('impl/build.gradle') << """ - ${getDefaultBuildScript('java-library')} + ${getDefaultProjectBuildScript('java-library')} version = '1.0' @@ -1004,14 +1004,14 @@ class ShadowPluginSpec extends BasePluginSpecification { shadowJar.minimize() """.stripIndent() - File serverOutput = getFile('impl/build/libs/impl-1.0-all.jar') + File serverOutput = file('impl/build/libs/impl-1.0-all.jar') when: - runWithDebug(':impl:shadowJar') + run(':impl:shadowJar') then: serverOutput.exists() - contains(serverOutput, [ + assertContains(serverOutput, [ 'api/UnusedEntity.class', ]) } @@ -1034,7 +1034,7 @@ class ShadowPluginSpec extends BasePluginSpecification { } """.stripIndent() - buildFile << """ + projectScriptFile << """ apply plugin: 'application' application { @@ -1078,7 +1078,7 @@ class ShadowPluginSpec extends BasePluginSpecification { } """.stripIndent() - settingsFile << "rootProject.name = 'myapp'" + settingsScriptFile << "rootProject.name = 'myapp'" when: BuildResult result = run('runShadow') @@ -1092,9 +1092,9 @@ class ShadowPluginSpec extends BasePluginSpecification { @Issue("https://github.com/GradleUp/shadow/issues/609") def "doesn't error when using application mainClass property"() { given: - buildFile.text = defaultBuildScript + projectScriptFile.text = getDefaultProjectBuildScript() - buildFile << """ + projectScriptFile << """ project.ext { aspectjVersion = '1.8.12' } @@ -1130,7 +1130,7 @@ class ShadowPluginSpec extends BasePluginSpecification { @Issue("https://github.com/GradleUp/shadow/pull/459") def 'exclude gradleApi() by default'() { given: - buildFile.text = getDefaultBuildScript('java-gradle-plugin', true, true) + projectScriptFile.text = getDefaultProjectBuildScript('java-gradle-plugin', true, true) file('src/main/java/my/plugin/MyPlugin.java') << """ package my.plugin; @@ -1150,16 +1150,16 @@ class ShadowPluginSpec extends BasePluginSpecification { run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - JarFile jar = new JarFile(output) + JarFile jar = new JarFile(outputShadowJar) assert jar.entries().collect().findAll { it.name.endsWith('.class') }.size() == 1 } @Issue("https://github.com/GradleUp/shadow/issues/1070") def 'can register a custom shadow jar task'() { - buildFile << """ + projectScriptFile << """ dependencies { testImplementation 'junit:junit:3.8.2' } @@ -1181,7 +1181,7 @@ class ShadowPluginSpec extends BasePluginSpecification { assert result.task(":testShadowJar").outcome == TaskOutcome.SUCCESS and: - def jarFile = new JarFile(output("shadow-1.0-tests.jar")) + def jarFile = new JarFile(file("build/libs/shadow-1.0-tests.jar")) assert jarFile.getEntry('junit') != null } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy index 4d5fa772d..f9c5d3cd8 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy @@ -26,7 +26,7 @@ class TransformerSpec extends BasePluginSpecification { .insert('META-INF/services/com.acme.Foo', 'two') .write() - buildFile << """ + projectScriptFile << """ import ${ServiceFileTransformer.name} $shadowJar { from('${escapedPath(one)}') @@ -41,17 +41,17 @@ class TransformerSpec extends BasePluginSpecification { run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - String text1 = getJarFileContents(output, 'META-INF/services/org.apache.maven.Shade') + String text1 = getJarFileContents(outputShadowJar, 'META-INF/services/org.apache.maven.Shade') assert text1.split("\\r?\\n").size() == 2 assert text1 == '''one # NOTE: No newline terminates this line/file two # NOTE: No newline terminates this line/file'''.stripIndent() and: - String text2 = getJarFileContents(output, 'META-INF/services/com.acme.Foo') + String text2 = getJarFileContents(outputShadowJar, 'META-INF/services/com.acme.Foo') assert text2.split("\\r?\\n").size() == 1 assert text2 == 'one' } @@ -64,7 +64,7 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def two = buildJar('two.jar').insert('META-INF/foo/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file').write() - buildFile << """ + projectScriptFile << """ import ${ServiceFileTransformer.name} $shadowJar { from('${escapedPath(one)}') @@ -79,10 +79,10 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - String text = getJarFileContents(output, 'META-INF/foo/org.apache.maven.Shade') + String text = getJarFileContents(outputShadowJar, 'META-INF/foo/org.apache.maven.Shade') assert text.split("\\r?\\n").size() == 2 assert text == '''one # NOTE: No newline terminates this line/file @@ -103,7 +103,7 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() .insert('META-INF/services/com.acme.Foo', 'two') .write() - buildFile << """ + projectScriptFile << """ $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') @@ -117,17 +117,17 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - String text1 = getJarFileContents(output, 'META-INF/services/org.apache.maven.Shade') + String text1 = getJarFileContents(outputShadowJar, 'META-INF/services/org.apache.maven.Shade') assert text1.split("\\r?\\n").size() == 2 assert text1 == '''one # NOTE: No newline terminates this line/file two # NOTE: No newline terminates this line/file'''.stripIndent() and: - String text2 = getJarFileContents(output, 'META-INF/services/com.acme.Foo') + String text2 = getJarFileContents(outputShadowJar, 'META-INF/services/com.acme.Foo') assert text2.split("\\r?\\n").size() == 1 assert text2 == 'one' } @@ -154,7 +154,7 @@ com.mysql.jdbc.Driver'''.stripIndent()) 'org.mortbay.log.Factory') .write() - buildFile << """ + projectScriptFile << """ $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') @@ -170,10 +170,10 @@ com.mysql.jdbc.Driver'''.stripIndent()) run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - String text1 = getJarFileContents(output, 'META-INF/services/java.sql.Driver') + String text1 = getJarFileContents(outputShadowJar, 'META-INF/services/java.sql.Driver') assert text1.split("\\r?\\n").size() == 4 assert text1 == '''oracle.jdbc.OracleDriver @@ -182,14 +182,14 @@ myapache.derby.jdbc.AutoloadedDriver com.mysql.jdbc.Driver'''.stripIndent() and: - String text2 = getJarFileContents(output, 'META-INF/services/myapache.axis.components.compiler.Compiler') + String text2 = getJarFileContents(outputShadowJar, 'META-INF/services/myapache.axis.components.compiler.Compiler') assert text2.split("\\r?\\n").size() == 2 assert text2 == '''myapache.axis.components.compiler.Javac org.apache.axis.components.compiler.Jikes'''.stripIndent() and: - String text3 = getJarFileContents(output, 'META-INF/services/org.apache.commons.logging.LogFactory') + String text3 = getJarFileContents(outputShadowJar, 'META-INF/services/org.apache.commons.logging.LogFactory') assert text3.split("\\r?\\n").size() == 2 assert text3 == '''myapache.commons.logging.impl.LogFactoryImpl @@ -204,7 +204,7 @@ org.mortbay.log.Factory'''.stripIndent() def two = buildJar('two.jar').insert('META-INF/foo/org.apache.maven.Shade', 'two # NOTE: No newline terminates this line/file').write() - buildFile << """ + projectScriptFile << """ $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') @@ -216,10 +216,10 @@ org.mortbay.log.Factory'''.stripIndent() run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - String text = getJarFileContents(output, 'META-INF/foo/org.apache.maven.Shade') + String text = getJarFileContents(outputShadowJar, 'META-INF/foo/org.apache.maven.Shade') assert text.split("\\r?\\n").size() == 2 assert text == '''one # NOTE: No newline terminates this line/file @@ -238,7 +238,7 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', 'two # NOTE: No newline terminates this line/file').publish() - buildFile << """ + projectScriptFile << """ dependencies { implementation 'shadow:two:1.0' implementation files('${escapedPath(one)}') @@ -256,10 +256,10 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - String text = getJarFileContents(output, 'META-INF/services/shadow.Shadow') + String text = getJarFileContents(outputShadowJar, 'META-INF/services/shadow.Shadow') assert text.split("\\r?\\n").size() == 3 assert text == '''three # NOTE: No newline terminates this line/file @@ -275,7 +275,7 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() def two = buildJar('two.jar').insert('test.properties', 'two # NOTE: No newline terminates this line/file').write() - buildFile << """ + projectScriptFile << """ import ${AppendingTransformer.name} $shadowJar { from('${escapedPath(one)}') @@ -290,10 +290,10 @@ two # NOTE: No newline terminates this line/file'''.stripIndent() run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - String text = getJarFileContents(output, 'test.properties') + String text = getJarFileContents(outputShadowJar, 'test.properties') assert text.split("\\r?\\n").size() == 2 assert text == '''one # NOTE: No newline terminates this line/file @@ -309,7 +309,7 @@ two # NOTE: No newline terminates this line/file def two = buildJar('two.jar').insert('test.properties', 'two # NOTE: No newline terminates this line/file').write() - buildFile << """ + projectScriptFile << """ $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') @@ -321,10 +321,10 @@ two # NOTE: No newline terminates this line/file run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - String text = getJarFileContents(output, 'test.properties') + String text = getJarFileContents(outputShadowJar, 'test.properties') assert text.split("\\r?\\n").size() == 2 assert text == '''one # NOTE: No newline terminates this line/file @@ -344,7 +344,7 @@ two # NOTE: No newline terminates this line/file } '''.stripIndent() - buildFile << """ + projectScriptFile << """ jar { manifest { attributes 'Main-Class': 'shadow.Main' @@ -357,10 +357,10 @@ two # NOTE: No newline terminates this line/file run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - JarInputStream jis = new JarInputStream(output.newInputStream()) + JarInputStream jis = new JarInputStream(outputShadowJar.newInputStream()) Manifest mf = jis.manifest jis.close() @@ -381,7 +381,7 @@ two # NOTE: No newline terminates this line/file } '''.stripIndent() - buildFile << """ + projectScriptFile << """ jar { manifest { attributes 'Main-Class': 'shadow.Main' @@ -401,10 +401,10 @@ two # NOTE: No newline terminates this line/file run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - JarInputStream jis = new JarInputStream(output.newInputStream()) + JarInputStream jis = new JarInputStream(outputShadowJar.newInputStream()) Manifest mf = jis.manifest jis.close() @@ -434,7 +434,7 @@ two # NOTE: No newline terminates this line/file '''.stripIndent() ).write() - buildFile << """ + projectScriptFile << """ import ${XmlAppendingTransformer.name} $shadowJar { @@ -450,10 +450,10 @@ two # NOTE: No newline terminates this line/file run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - String text = getJarFileContents(output, 'properties.xml') + String text = getJarFileContents(outputShadowJar, 'properties.xml') assert text.replaceAll('\r\n', '\n') == ''' @@ -477,7 +477,7 @@ two # NOTE: No newline terminates this line/file } '''.stripIndent() - buildFile << """ + projectScriptFile << """ jar { manifest { attributes 'Main-Class': 'shadow.Main' @@ -497,12 +497,12 @@ two # NOTE: No newline terminates this line/file run('jar', 'shadowJar') then: - File jar = getFile('build/libs/shadow-1.0.jar') + File jar = file('build/libs/shadow-1.0.jar') assert jar.exists() - assert output.exists() + assert outputShadowJar.exists() then: 'Check contents of Shadow jar manifest' - JarInputStream jis = new JarInputStream(output.newInputStream()) + JarInputStream jis = new JarInputStream(outputShadowJar.newInputStream()) Manifest mf = jis.manifest assert mf @@ -537,7 +537,7 @@ two # NOTE: No newline terminates this line/file } '''.stripIndent() - buildFile << """ + projectScriptFile << """ jar { manifest { attributes 'Main-Class': 'shadow.Main' @@ -557,12 +557,12 @@ two # NOTE: No newline terminates this line/file run('jar', 'shadowJar') then: - File jar = getFile('build/libs/shadow-1.0.jar') + File jar = file('build/libs/shadow-1.0.jar') assert jar.exists() - assert output.exists() + assert outputShadowJar.exists() then: 'Check contents of Shadow jar manifest' - JarInputStream jis = new JarInputStream(output.newInputStream()) + JarInputStream jis = new JarInputStream(outputShadowJar.newInputStream()) Manifest mf = jis.manifest assert mf @@ -600,7 +600,7 @@ moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() - buildFile << """ + projectScriptFile << """ import ${GroovyExtensionModuleTransformer.name} $shadowJar { from('${escapedPath(one)}') @@ -613,10 +613,10 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - def text = getJarFileContents(output, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') + def text = getJarFileContents(outputShadowJar, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') def props = new Properties() props.load(new StringReader(text)) assert props.getProperty('moduleName') == 'MergedByShadowJar' @@ -641,7 +641,7 @@ moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() - buildFile << """ + projectScriptFile << """ import ${GroovyExtensionModuleTransformer.name} $shadowJar { from('${escapedPath(one)}') @@ -654,17 +654,17 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( run('shadowJar') then: - output.exists() + outputShadowJar.exists() and: - def text = getJarFileContents(output, 'META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule') + def text = getJarFileContents(outputShadowJar, 'META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule') def props = new Properties() props.load(new StringReader(text)) props.getProperty('moduleName') == 'MergedByShadowJar' props.getProperty('moduleVersion') == '1.0.0' props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' - doesNotContain(output, ['META-INF/services/org.codehaus.groovy.runtime.ExtensionModule']) + assertDoesNotContain(outputShadowJar, ['META-INF/services/org.codehaus.groovy.runtime.ExtensionModule']) } def 'Groovy extension module transformer short syntax'() { @@ -683,7 +683,7 @@ moduleVersion=2.3.5 extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() - buildFile << """ + projectScriptFile << """ $shadowJar { from('${escapedPath(one)}') from('${escapedPath(two)}') @@ -695,10 +695,10 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() and: - def text = getJarFileContents(output, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') + def text = getJarFileContents(outputShadowJar, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') def props = new Properties() props.load(new StringReader(text)) assert props.getProperty('moduleName') == 'MergedByShadowJar' @@ -713,7 +713,7 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( if (configuration.contains('test/some.file')) { file('test/some.file') << 'some content' } - buildFile << """ + projectScriptFile << """ import com.github.jengelman.gradle.plugins.shadow.transformers.${transformer} $shadowJar { @@ -725,7 +725,7 @@ staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write( run('shadowJar') then: - assert output.exists() + assert outputShadowJar.exists() where: transformer | configuration diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy index da4afc8fc..d227f8e79 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy @@ -19,7 +19,7 @@ abstract class AbstractCachingSpec extends BasePluginSpecification { def setup() { // Use a test-specific build cache directory. This ensures that we'll only use cached outputs generated during this // test and we won't accidentally use cached outputs from a different test or a different build. - settingsFile << """ + settingsScriptFile << """ buildCache { local { directory = new File(rootDir, 'build-cache') @@ -29,8 +29,8 @@ abstract class AbstractCachingSpec extends BasePluginSpecification { } void changeConfigurationTo(String content) { - buildFile.text = getDefaultBuildScript('java', true, true) - buildFile << content + projectScriptFile.text = getDefaultProjectBuildScript('java', true, true) + projectScriptFile << content } BuildResult runWithCacheEnabled(String... arguments) { @@ -59,7 +59,7 @@ abstract class AbstractCachingSpec extends BasePluginSpecification { void copyToAlternateDir() { FileUtils.deleteDirectory(alternateDir.toFile()) FileUtils.forceMkdir(alternateDir.toFile()) - FileUtils.copyDirectory(dir.toFile(), alternateDir.toFile()) + FileUtils.copyDirectory(root.toFile(), alternateDir.toFile()) } void assertShadowJarIsCachedAndRelocatable() { @@ -78,8 +78,8 @@ abstract class AbstractCachingSpec extends BasePluginSpecification { } void deleteOutputs() { - if (output.exists()) { - assert output.delete() + if (outputShadowJar.exists()) { + assert outputShadowJar.delete() } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy index 69bb06c88..5c55d58a8 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy @@ -1,13 +1,17 @@ package com.github.jengelman.gradle.plugins.shadow.caching class MinimizationCachingSpec extends AbstractCachingSpec { - File output @Override String getShadowJarTask() { return ":server:shadowJar" } + @Override + File getOutputShadowJar() { + return file('server/build/libs/server-all.jar') + } + /** * Ensure that we get a cache miss when minimization is added and that caching works with minimization */ @@ -34,19 +38,17 @@ class MinimizationCachingSpec extends AbstractCachingSpec { """.stripIndent() file('server/build.gradle') << """ - $defaultBuildScript + $defaultProjectBuildScript dependencies { implementation project(':client') } """.stripIndent() - output = getFile('server/build/libs/server-all.jar') - when: assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'junit/framework/Test.class', 'client/Client.class' @@ -65,22 +67,22 @@ class MinimizationCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'junit/framework/Test.class' ]) - doesNotContain(output, ['client/Client.class']) + assertDoesNotContain(outputShadowJar, ['client/Client.class']) when: assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'junit/framework/Test.class' ]) - doesNotContain(output, ['client/Client.class']) + assertDoesNotContain(outputShadowJar, ['client/Client.class']) } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy index 1fa573932..8fbc18a15 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy @@ -6,7 +6,7 @@ class RelocationCachingSpec extends AbstractCachingSpec { */ def 'shadowJar is cached correctly when relocation is added'() { given: - buildFile << """ + projectScriptFile << """ dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -22,8 +22,8 @@ class RelocationCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'junit/framework/Test.class' ]) @@ -39,14 +39,14 @@ class RelocationCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/junit/framework/Test.class' ]) and: - doesNotContain(output, [ + assertDoesNotContain(outputShadowJar, [ 'junit/framework/Test.class' ]) @@ -54,14 +54,14 @@ class RelocationCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/junit/framework/Test.class' ]) and: - doesNotContain(output, [ + assertDoesNotContain(outputShadowJar, [ 'junit/framework/Test.class' ]) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy index 0c3b4efc4..4c786054c 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy @@ -10,7 +10,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { URL artifact = this.class.classLoader.getResource('test-artifact-1.0-SNAPSHOT.jar') URL project = this.class.classLoader.getResource('test-project-1.0-SNAPSHOT.jar') - buildFile << """ + projectScriptFile << """ $shadowJar { from('${artifact.path}') from('${project.path}') @@ -21,13 +21,13 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - assert output.exists() + assert outputShadowJar.exists() when: assertShadowJarIsCachedAndRelocatable() then: - assert output.exists() + assert outputShadowJar.exists() when: changeConfigurationTo """ @@ -38,7 +38,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - assert output.exists() + assert outputShadowJar.exists() } /** @@ -49,7 +49,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { URL artifact = this.class.classLoader.getResource('test-artifact-1.0-SNAPSHOT.jar') URL project = this.class.classLoader.getResource('test-project-1.0-SNAPSHOT.jar') - buildFile << """ + projectScriptFile << """ $shadowJar { from('${artifact.path}') from('${project.path}') @@ -60,7 +60,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - assert output.exists() + assert outputShadowJar.exists() when: changeConfigurationTo """ @@ -73,8 +73,8 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - assert !output.exists() - assert getFile("build/libs/foo-1.0-all.jar").exists() + assert !outputShadowJar.exists() + assert file("build/libs/foo-1.0-all.jar").exists() } /** @@ -82,7 +82,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { */ def 'shadowJar is cached correctly when using includes/excludes'() { given: - buildFile << """ + projectScriptFile << """ dependencies { implementation 'junit:junit:3.8.2' } $shadowJar { @@ -110,8 +110,8 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'server/Util.class' ]) @@ -128,13 +128,13 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) and: - doesNotContain(output, [ + assertDoesNotContain(outputShadowJar, [ 'server/Util.class', 'junit/framework/Test.class' ]) @@ -143,13 +143,13 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) and: - doesNotContain(output, [ + assertDoesNotContain(outputShadowJar, [ 'server/Util.class', 'junit/framework/Test.class' ]) @@ -160,7 +160,7 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { */ def 'shadowJar is cached correctly when using dependency includes/excludes'() { given: - buildFile << """ + projectScriptFile << """ dependencies { implementation 'junit:junit:3.8.2' } """.stripIndent() @@ -176,8 +176,8 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'junit/framework/Test.class' ]) @@ -195,13 +195,13 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) and: - doesNotContain(output, [ + assertDoesNotContain(outputShadowJar, [ 'junit/framework/Test.class' ]) @@ -209,13 +209,13 @@ class ShadowJarCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) and: - doesNotContain(output, [ + assertDoesNotContain(outputShadowJar, [ 'junit/framework/Test.class' ]) } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy index b937815c1..e48996e52 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy @@ -18,7 +18,7 @@ class TransformCachingSpec extends AbstractCachingSpec { public class Server {} """.stripIndent() - buildFile << """ + projectScriptFile << """ import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import org.apache.tools.zip.ZipOutputStream @@ -56,8 +56,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) @@ -65,8 +65,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) } @@ -87,8 +87,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) @@ -104,8 +104,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) @@ -113,8 +113,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) @@ -130,8 +130,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) @@ -139,8 +139,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) } @@ -162,8 +162,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) @@ -179,8 +179,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/bar.properties' ]) @@ -189,8 +189,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/bar.properties' ]) @@ -209,8 +209,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/baz.properties' ]) @@ -219,8 +219,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/baz.properties' ]) @@ -243,8 +243,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) @@ -260,8 +260,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/bar.xml' ]) @@ -270,8 +270,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/bar.xml' ]) @@ -290,8 +290,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/baz.xml' ]) @@ -300,8 +300,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class', 'foo/baz.xml' ]) @@ -323,8 +323,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) @@ -338,8 +338,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarExecutes() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) @@ -347,8 +347,8 @@ class TransformCachingSpec extends AbstractCachingSpec { assertShadowJarIsCachedAndRelocatable() then: - output.exists() - contains(output, [ + outputShadowJar.exists() + assertContains(outputShadowJar, [ 'server/Server.class' ]) } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt index 5160d4a04..6fe3f2464 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt @@ -103,7 +103,7 @@ class ApplicationTest : BasePluginTest() { } """.trimIndent(), ) - buildScript.appendText( + projectScriptPath.appendText( """ apply plugin: 'application' $projectBlock @@ -119,8 +119,8 @@ class ApplicationTest : BasePluginTest() { } """.trimIndent(), ) - settingsScript.writeText( - getSettingsBuildScript( + settingsScriptPath.writeText( + getDefaultSettingsBuildScript( startBlock = settingsBlock, endBlock = "rootProject.name = 'myapp'", ), diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index ff828078a..7f047228b 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -16,6 +16,7 @@ import kotlin.io.path.createFile import kotlin.io.path.createTempDirectory import kotlin.io.path.deleteRecursively import kotlin.io.path.exists +import kotlin.io.path.extension import kotlin.io.path.readText import kotlin.io.path.toPath import kotlin.io.path.writeText @@ -39,13 +40,8 @@ abstract class BasePluginTest { .use(testJar) .publish() - buildScript.writeText( - getProjectBuildScript( - groupInfo = "group = 'shadow'", - versionInfo = "version = '1.0'", - ), - ) - settingsScript.writeText(getSettingsBuildScript()) + projectScriptPath.writeText(getDefaultProjectBuildScript(withGroup = true, withVersion = true)) + settingsScriptPath.writeText(getDefaultSettingsBuildScript()) } @ExperimentalPathApi @@ -56,14 +52,16 @@ abstract class BasePluginTest { root.deleteRecursively() } - println(buildScript.readText()) + println(projectScriptPath.readText()) } - fun getProjectBuildScript( + fun getDefaultProjectBuildScript( javaPlugin: String = "java", - groupInfo: String = "", - versionInfo: String = "", + withGroup: Boolean = false, + withVersion: Boolean = false, ): String { + val groupInfo = if (withGroup) "group = 'shadow'" else "" + val versionInfo = if (withVersion) "version = '1.0'" else "" return """ plugins { id('$javaPlugin') @@ -74,7 +72,7 @@ abstract class BasePluginTest { """.trimIndent() + System.lineSeparator() } - fun getSettingsBuildScript( + fun getDefaultSettingsBuildScript( startBlock: String = "", endBlock: String = "rootProject.name = 'shadow'", ): String { @@ -117,10 +115,10 @@ abstract class BasePluginTest { open val shadowJarTask = SHADOW_JAR_TASK_NAME open val runShadowTask = SHADOW_RUN_TASK_NAME - val buildScript: Path + val projectScriptPath: Path get() = path("build.gradle") - val settingsScript: Path + val settingsScriptPath: Path get() = path("settings.gradle") val outputShadowJar: Path @@ -128,10 +126,12 @@ abstract class BasePluginTest { fun path(path: String): Path { return root.resolve(path).also { - if (!it.exists()) { - it.parent.createDirectories() - it.createFile() - } + val extension = it.extension + // Binary files should not be created, text files should be created. + if (it.exists() || extension == "jar" || extension == "zip") return@also + + it.parent.createDirectories() + it.createFile() } } @@ -189,12 +189,12 @@ abstract class BasePluginTest { fun writeClientAndServerModules( serverShadowBlock: String = "", ) { - settingsScript.appendText( + settingsScriptPath.appendText( """ include 'client', 'server' """.trimIndent(), ) - buildScript.writeText("") + projectScriptPath.writeText("") path("client/src/main/java/client/Client.java").writeText( """ @@ -204,7 +204,7 @@ abstract class BasePluginTest { ) path("client/build.gradle").writeText( """ - ${getProjectBuildScript("java", versionInfo = "version = '1.0'")} + ${getDefaultProjectBuildScript("java", withVersion = true)} dependencies { implementation 'junit:junit:3.8.2' } """.trimIndent(), ) @@ -218,7 +218,7 @@ abstract class BasePluginTest { ) path("server/build.gradle").writeText( """ - ${getProjectBuildScript("java", versionInfo = "version = '1.0'")} + ${getDefaultProjectBuildScript("java", withVersion = true)} dependencies { implementation project(':client') } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt index 851994739..33fbcc040 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt @@ -20,7 +20,7 @@ class ConfigurationCacheSpec : BasePluginTest() { super.setup() publishArtifactA() publishArtifactB() - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'shadow:a:1.0' @@ -43,7 +43,7 @@ class ConfigurationCacheSpec : BasePluginTest() { """.trimIndent(), ) - buildScript.appendText( + projectScriptPath.appendText( """ apply plugin: 'application' @@ -64,7 +64,7 @@ class ConfigurationCacheSpec : BasePluginTest() { @Test fun configurationCachingSupportsExcludes() { - buildScript.appendText( + projectScriptPath.appendText( """ $shadowJar { exclude 'a2.properties' @@ -96,9 +96,9 @@ class ConfigurationCacheSpec : BasePluginTest() { } """.trimIndent(), ) - val output = path("server/build/libs/server-1.0-all.jar") run(shadowJarTask) + val output = path("server/build/libs/server-1.0-all.jar") output.deleteExisting() val result = run(shadowJarTask) @@ -116,7 +116,7 @@ class ConfigurationCacheSpec : BasePluginTest() { @Test fun configurationCachingOfConfigurationsIsUpToDate() { - settingsScript.appendText( + settingsScriptPath.appendText( """ include 'lib' """.trimIndent(), @@ -130,7 +130,7 @@ class ConfigurationCacheSpec : BasePluginTest() { ) path("lib/build.gradle").writeText( """ - ${getProjectBuildScript()} + ${getDefaultProjectBuildScript()} dependencies { implementation 'junit:junit:3.8.2' } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index a639f7b11..1eead9284 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -19,7 +19,7 @@ class FilteringTest : BasePluginTest() { publishArtifactA() publishArtifactB() - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'shadow:a:1.0' @@ -40,7 +40,7 @@ class FilteringTest : BasePluginTest() { @Test fun excludeFiles() { - buildScript.appendText( + projectScriptPath.appendText( """ $shadowJar { exclude 'a2.properties' @@ -73,7 +73,7 @@ class FilteringTest : BasePluginTest() { @Test fun excludeDependencyUsingWildcardSyntax() { publishArtifactCD() - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'shadow:d:1.0' @@ -100,9 +100,9 @@ class FilteringTest : BasePluginTest() { commonAssertions() - val replaced = buildScript.readText() + val replaced = projectScriptPath.readText() .replace("exclude(dependency('shadow:d:1.0'))", "exclude(dependency('shadow:c:1.0'))") - buildScript.writeText(replaced) + projectScriptPath.writeText(replaced) val result = run(shadowJarTask) assertThat(result.task(":shadowJar")).isNotNull() @@ -126,9 +126,9 @@ class FilteringTest : BasePluginTest() { commonAssertions() - val replaced = buildScript.readText() + val replaced = projectScriptPath.readText() .replace("exclude(dependency('shadow:d:1.0'))", "exclude 'a.properties'") - buildScript.writeText(replaced) + projectScriptPath.writeText(replaced) val result = run(shadowJarTask) @@ -147,7 +147,7 @@ class FilteringTest : BasePluginTest() { @Test fun includeDependencyAndExcludeOthers() { publishArtifactCD() - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'shadow:d:1.0' @@ -188,8 +188,8 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - val serverOutput = path("server/build/libs/server-1.0-all.jar") run(":server:$shadowJarTask") + val serverOutput = path("server/build/libs/server-1.0-all.jar") assertThat(serverOutput).exists() assertDoesNotContain( @@ -212,8 +212,8 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - val serverOutput = path("server/build/libs/server-1.0-all.jar") run(":server:$shadowJarTask") + val serverOutput = path("server/build/libs/server-1.0-all.jar") assertThat(serverOutput).exists() assertDoesNotContain( @@ -228,7 +228,7 @@ class FilteringTest : BasePluginTest() { @Test fun verifyExcludePrecedenceOverInclude() { - buildScript.appendText( + projectScriptPath.appendText( """ $shadowJar { include '*.jar' @@ -261,7 +261,7 @@ class FilteringTest : BasePluginTest() { } private fun dependOnAndExcludeArtifactD() { - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'shadow:d:1.0' diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index e502abfe4..a80671375 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -15,7 +15,7 @@ class RelocationTest : BasePluginTest() { @Test fun defaultEnableRelocation() { - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'junit:junit:3.8.2' @@ -55,7 +55,7 @@ class RelocationTest : BasePluginTest() { */ @Test fun relocateDependencyFiles() { - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'junit:junit:3.8.2' @@ -121,7 +121,7 @@ class RelocationTest : BasePluginTest() { @Test fun relocateDependencyFilesWithFiltering() { - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'junit:junit:3.8.2' @@ -185,7 +185,7 @@ class RelocationTest : BasePluginTest() { */ @Test fun remapClassNamesForRelocatedFilesInProjectSource() { - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'junit:junit:3.8.2' @@ -268,7 +268,7 @@ class RelocationTest : BasePluginTest() { path("app/build.gradle").writeText( """ - ${getProjectBuildScript()} + ${getDefaultProjectBuildScript()} dependencies { implementation project(':core') } @@ -290,7 +290,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - settingsScript.appendText( + settingsScriptPath.appendText( """ include 'core', 'app' """.trimIndent(), @@ -332,7 +332,7 @@ class RelocationTest : BasePluginTest() { ) path("src/main/resources/foo/foo.properties").writeText("name=foo") - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'shadow:dep:1.0' @@ -368,7 +368,7 @@ class RelocationTest : BasePluginTest() { */ @Test fun doesNotErrorOnRelocatingJava9Classes() { - buildScript.appendText( + projectScriptPath.appendText( """ dependencies { implementation 'org.slf4j:slf4j-api:1.7.21' From 009ce032dcd24ded1d1eee796524af81d7f2a701 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 1 Jan 2025 02:34:15 -0500 Subject: [PATCH 118/941] Polish doc tests (#1124) --- src/docs/application-plugin/README.md | 8 +++ .../reproducible-builds/README.md | 2 +- src/docs/getting-started/README.md | 4 +- src/docs/plugins/README.md | 3 - src/docs/publishing/README.md | 2 +- .../doc/executor/GroovyBuildExecutor.kt | 59 ++++++++++--------- .../shadow/doc/fixture/GroovyDslFixture.kt | 16 +---- .../shadow/doc/fixture/SnippetFixture.kt | 2 - 8 files changed, 44 insertions(+), 52 deletions(-) diff --git a/src/docs/application-plugin/README.md b/src/docs/application-plugin/README.md index 60acd35c9..c1e8110ae 100644 --- a/src/docs/application-plugin/README.md +++ b/src/docs/application-plugin/README.md @@ -28,6 +28,14 @@ It can be configured the same as any other `JavaExec` task. ```groovy // Configuring the runShadow Task +apply plugin: 'java' +apply plugin: 'application' +apply plugin: 'com.gradleup.shadow' + +application { + mainClass = 'myapp.Main' +} + tasks.named('runShadow', com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec) { args 'foo' } diff --git a/src/docs/configuration/reproducible-builds/README.md b/src/docs/configuration/reproducible-builds/README.md index 79f7505e8..aaec5858d 100644 --- a/src/docs/configuration/reproducible-builds/README.md +++ b/src/docs/configuration/reproducible-builds/README.md @@ -3,7 +3,7 @@ By default, JAR files generated by Gradle (with or without Shadow) for a single project with the same source code may not be identical to each other. Sometimes it's desireable to configure a project to consistently output a byte-for-byte identical JAR on every build. Gradle supports this with the following configuration, and Shadow will correctly respect these settings too: ```groovy -tasks.withType(AbstractArchiveTask) { +tasks.withType(AbstractArchiveTask).configureEach { preserveFileTimestamps = false reproducibleFileOrder = true } diff --git a/src/docs/getting-started/README.md b/src/docs/getting-started/README.md index 8963fe0aa..6df8741a8 100644 --- a/src/docs/getting-started/README.md +++ b/src/docs/getting-started/README.md @@ -2,8 +2,8 @@ ```groovy no-run plugins { - id 'com.gradleup.shadow' version '@version@' id 'java' + id 'com.gradleup.shadow' version '@version@' } ``` @@ -38,8 +38,8 @@ buildscript { } } -apply plugin: 'com.gradleup.shadow' apply plugin: 'java' +apply plugin: 'com.gradleup.shadow' ```

diff --git a/src/docs/plugins/README.md b/src/docs/plugins/README.md index 2ea99dd7c..b4b6a4840 100644 --- a/src/docs/plugins/README.md +++ b/src/docs/plugins/README.md @@ -11,9 +11,6 @@ and `relocationPrefix` settings on any `ShadowJar` task. A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the `shadowJar` task for relocation. ```groovy -apply plugin: 'java' -apply plugin: 'com.gradleup.shadow' - dependencies { shadow localGroovy() shadow gradleApi() diff --git a/src/docs/publishing/README.md b/src/docs/publishing/README.md index 596fe3eeb..d87ccf8fc 100644 --- a/src/docs/publishing/README.md +++ b/src/docs/publishing/README.md @@ -21,7 +21,7 @@ publishing { } repositories { maven { - url "https://repo.myorg.com" + url = "https://repo.myorg.com" } } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt index 68c2dc7e2..556f93fb2 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt @@ -5,32 +5,53 @@ import java.nio.file.Path import kotlin.io.path.createDirectory import kotlin.io.path.writeText import org.gradle.testkit.runner.GradleRunner -import org.intellij.lang.annotations.Language class GroovyBuildExecutor( override val fixture: SnippetFixture, private val importExtractor: (String) -> List, - private val arguments: Array = arrayOf("build", "-m"), ) : SnippetExecutor { override fun execute(tempDir: Path, snippet: String) { - tempDir.resolve("settings.gradle").writeText(settingsBuildText) - tempDir.addSubProject("api", projectBuildText) + tempDir.resolve("settings.gradle").writeText( + """ + dependencyResolutionManagement { + repositories { + mavenLocal() + mavenCentral() + } + } + include 'api', 'main' + """.trimIndent(), + ) - val (imports, snippetWithoutImports) = importExtractor(snippet) - val fullSnippet = imports + fixture.pre + '\n' + snippetWithoutImports + '\n' + fixture.post + val apiScript = """ + plugins { + id 'java' + id 'com.gradleup.shadow' + } + """.trimIndent() + tempDir.addSubProject("api", apiScript) - tempDir.addSubProject("main", fullSnippet) + val (imports, snippetWithoutImports) = importExtractor(snippet) + val mainScript = buildString { + append(imports) + append(System.lineSeparator()) + append(fixture.pre) + append(System.lineSeparator()) + append(snippetWithoutImports) + append(System.lineSeparator()) + }.trimIndent() + tempDir.addSubProject("main", mainScript) try { GradleRunner.create() .withProjectDir(tempDir.toFile()) .withPluginClasspath() .forwardOutput() - .withArguments(*arguments) + .withArguments("--warning-mode=fail", "build") .build() } catch (t: Throwable) { - throw RuntimeException("Failed to execute snippet:\n\n$fullSnippet", t) + throw RuntimeException("Failed to execute snippet:\n\n$mainScript", t) } } @@ -40,24 +61,4 @@ class GroovyBuildExecutor( .resolve("build.gradle") .writeText(buildScriptText) } - - private companion object { - @Language("Groovy") - private val settingsBuildText = """ - rootProject.name = 'shadowTest' - include 'api', 'main' - """.trimIndent() - - @Language("Groovy") - private val projectBuildText = """ - plugins { - id 'java' - id 'com.gradleup.shadow' - } - repositories { - mavenLocal() - mavenCentral() - } - """.trimIndent() - } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt index b4e60e4c3..e7dacf74d 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt @@ -1,26 +1,14 @@ package com.github.jengelman.gradle.plugins.shadow.doc.fixture -import org.intellij.lang.annotations.Language - object GroovyDslFixture : SnippetFixture { - @Language("Groovy") override val pre: String = """ plugins { - id("java") - id("com.gradleup.shadow") - id("application") - } - repositories { - mavenLocal() - mavenCentral() + id 'java' + id 'com.gradleup.shadow' } - version = "1.0" - group = "shadow" """.trimIndent() - override val post: String = "" - val importsExtractor: (String) -> List = { snippet -> val imports = StringBuilder() val scriptMinusImports = StringBuilder() diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt index a554ad205..8a20af065 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt @@ -2,6 +2,4 @@ package com.github.jengelman.gradle.plugins.shadow.doc.fixture interface SnippetFixture { val pre: String - - val post: String } From b67c5cc169f323a1e578e4cdf8475c6e6c56088e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 1 Jan 2025 06:33:23 -0500 Subject: [PATCH 119/941] Migrate functional tests to Kotlin and Junit part 2 (#1123) * Convert part of ShadowPluginSpec * Fix useMinimizeWithTransitiveDependenciesWithApiScope * Convert the rest of ShadowPluginSpec * Optimize Java version checker for compatibleWithMinGradleVersion * Fixes * Add comments * Remove unused buildJar * Fix shadowCopy on Windows * Merge duplicate overloads * Correct tests * Rearrange * Tweak and note `excludeProjectFromMinimizeShallNotExcludeTransitiveDependenciesThatAreUsedInSubproject` * Reuse serverShadowJarTask and outputServerShadowJar * Replace runShadowTask hardcode * Correct `writeClientAndServerModules` usages * Correct `writeApiLibAndImplModules` usages * Rearrange --- .../plugins/shadow/ShadowPluginSpec.groovy | 1187 ----------------- .../gradle/plugins/shadow/BasePluginTest.kt | 104 +- .../plugins/shadow/ConfigurationCacheSpec.kt | 9 +- .../gradle/plugins/shadow/FilteringTest.kt | 18 +- .../gradle/plugins/shadow/ShadowPluginTest.kt | 814 +++++++++++ 5 files changed, 918 insertions(+), 1214 deletions(-) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy deleted file mode 100644 index 5117778e4..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/ShadowPluginSpec.groovy +++ /dev/null @@ -1,1187 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import org.gradle.api.JavaVersion -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.plugins.JavaPlugin -import org.gradle.testfixtures.ProjectBuilder -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.TaskOutcome -import spock.lang.Ignore -import spock.lang.IgnoreIf -import spock.lang.Issue -import spock.lang.Unroll - -import java.util.jar.Attributes -import java.util.jar.JarFile - -class ShadowPluginSpec extends BasePluginSpecification { - - def 'apply plugin'() { - given: - String projectName = 'myshadow' - String version = '1.0.0' - - Project project = ProjectBuilder.builder().withName(projectName).build() - project.version = version - - when: - project.plugins.apply(ShadowPlugin) - - then: - project.plugins.hasPlugin(ShadowPlugin) - project.plugins.hasPlugin(com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin) - - and: - assert !project.tasks.findByName('shadowJar') - - when: - project.plugins.apply(JavaPlugin) - - then: - ShadowJar shadow = project.tasks.findByName('shadowJar') - assert shadow - assert shadow.archiveBaseName.get() == projectName - assert shadow.destinationDirectory.get().asFile == new File(project.layout.buildDirectory.asFile.get(), 'libs') - assert shadow.archiveVersion.get() == version - assert shadow.archiveClassifier.get() == 'all' - assert shadow.archiveExtension.get() == 'jar' - - and: - Configuration shadowConfig = project.configurations.findByName('shadow') - assert shadowConfig - shadowConfig.artifacts.file.contains(shadow.archiveFile.get().asFile) - - } - - @IgnoreIf( - reason = "Gradle 8.3 doesn't support Java 21.", - value = { - // Gradle 8.3 doesn't support Java 21. - JavaVersion.current().majorVersion.toInteger() >= 21 - } - ) - @Unroll - def 'Compatible with Gradle #version'() { - given: - def one = buildJar('one.jar').insert('META-INF/services/shadow.Shadow', - 'one # NOTE: No newline terminates this line/file').write() - - repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', - 'two # NOTE: No newline terminates this line/file').publish() - - projectScriptFile << """ - dependencies { - implementation 'junit:junit:3.8.2' - implementation files('${escapedPath(one)}') - } - - $shadowJar { - mergeServiceFiles() - } - """.stripIndent() - - when: - run(['shadowJar']) { - it.withGradleVersion(version) - } - - then: - assert outputShadowJar.exists() - - where: - version << ['8.3'] - } - - def 'Error in Gradle versions < 8.3'() { - given: - projectScriptFile << """ - dependencies { - implementation 'junit:junit:3.8.2' - } - - $shadowJar { - mergeServiceFiles() - } - """.stripIndent() - - expect: - runWithFailure(['shadowJar']) { - it.withGradleVersion('7.0') - } - } - - def 'shadow copy'() { - given: - URL artifact = this.class.classLoader.getResource('test-artifact-1.0-SNAPSHOT.jar') - URL project = this.class.classLoader.getResource('test-project-1.0-SNAPSHOT.jar') - - projectScriptFile << """ - $shadowJar { - from('${artifact.path}') - from('${project.path}') - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - } - - def 'include project sources'() { - given: - file('src/main/java/shadow/Passed.java') << ''' - package shadow; - public class Passed {} - '''.stripIndent() - - projectScriptFile << """ - dependencies { implementation 'junit:junit:3.8.2' } - - $shadowJar { - archiveBaseName = 'shadow' - archiveClassifier = null - archiveVersion = null - archiveVersion.convention(null) - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assertContains(file("build/libs/shadow.jar"), ['shadow/Passed.class', 'junit/framework/Test.class']) - - and: - assertDoesNotContain(file("build/libs/shadow.jar"), ['/']) - } - - def 'include project dependencies'() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - - file('client/build.gradle') << """ - apply plugin: 'java' - - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - - import client.Client; - - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - $defaultProjectBuildScript - - dependencies { implementation project(':client') } - - """.stripIndent() - - File serverOutput = file('server/build/libs/server-all.jar') - - when: - run(':server:shadowJar') - - then: - serverOutput.exists() - assertContains(serverOutput, [ - 'client/Client.class', - 'server/Server.class', - 'junit/framework/Test.class' - ]) - } - - /** - * 'Server' depends on 'Client'. 'junit' is independent. - * The minimize shall remove 'junit'. - */ - def 'minimize by keeping only transitive dependencies'() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - - file('client/build.gradle') << """ - apply plugin: 'java' - - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - - import client.Client; - - public class Server { - private final String client = Client.class.getName(); - } - """.stripIndent() - - file('server/build.gradle') << """ - $defaultProjectBuildScript - - $shadowJar { - minimize() - } - - dependencies { implementation project(':client') } - """.stripIndent() - - File serverOutput = file('server/build/libs/server-all.jar') - - when: - run(':server:shadowJar') - - then: - serverOutput.exists() - assertContains(serverOutput, [ - 'client/Client.class', - 'server/Server.class' - ]) - assertDoesNotContain(serverOutput, ['junit/framework/Test.class']) - } - - /** - * 'Client', 'Server' and 'junit' are independent. - * 'junit' is excluded from the minimize. - * The minimize shall remove 'Client' but not 'junit'. - */ - def 'exclude a dependency from minimize'() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - - file('client/build.gradle') << """ - apply plugin: 'java' - - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - $defaultProjectBuildScript - - $shadowJar { - minimize { - exclude(dependency('junit:junit:.*')) - } - } - - dependencies { implementation project(':client') } - """.stripIndent() - - File serverOutput = file('server/build/libs/server-all.jar') - - when: - run(':server:shadowJar') - - then: - serverOutput.exists() - assertContains(serverOutput, [ - 'server/Server.class', - 'junit/framework/Test.class' - ]) - assertDoesNotContain(serverOutput, ['client/Client.class']) - } - - /** - * 'Client', 'Server' and 'junit' are independent. - * Unused classes of 'client' and theirs dependencies shouldn't be removed. - */ - def 'exclude a project from minimize '() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - - file('client/build.gradle') << """ - apply plugin: 'java' - - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - $defaultProjectBuildScript - - $shadowJar { - minimize { - exclude(project(':client')) - } - } - - dependencies { implementation project(':client') } - """.stripIndent() - - File serverOutput = file('server/build/libs/server-all.jar') - - when: - run(':server:shadowJar') - - then: - assertContains(serverOutput, [ - 'client/Client.class', - 'server/Server.class' - ]) - } - - /** - * 'Client', 'Server' and 'junit' are independent. - * Unused classes of 'client' and theirs dependencies shouldn't be removed. - */ - def 'exclude a project from minimize - shall not exclude transitive dependencies that are used in subproject'() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - import junit.framework.TestCase; - public class Client extends TestCase { - public static void main(String[] args) {} - } - """.stripIndent() - - file('client/build.gradle') << """ - apply plugin: 'java' - - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - $defaultProjectBuildScript - - $shadowJar { - minimize { - exclude(project(':client')) - } - } - - dependencies { implementation project(':client') } - """.stripIndent() - - File serverOutput = file('server/build/libs/server-all.jar') - - when: - run(':server:shadowJar') - - then: - assertContains(serverOutput, [ - 'client/Client.class', - 'server/Server.class', - 'junit/framework/TestCase.class' - ]) - } - - /** - * 'Client', 'Server' and 'junit' are independent. - * Unused classes of 'client' and theirs dependencies shouldn't be removed. - */ - def 'exclude a project from minimize - shall not exclude transitive dependencies from subproject that are not used'() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client { } - """.stripIndent() - - file('client/build.gradle') << """ - apply plugin: 'java' - - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - $defaultProjectBuildScript - - $shadowJar { - minimize { - exclude(project(':client')) - } - } - - dependencies { implementation project(':client') } - """.stripIndent() - - File serverOutput = file('server/build/libs/server-all.jar') - - when: - run(':server:shadowJar') - - then: - assertContains(serverOutput, [ - 'client/Client.class', - 'server/Server.class', - 'junit/framework/TestCase.class' - ]) - } - - - /** - * 'api' used as api for 'impl', and depended on 'lib'. 'junit' is independent. - * The minimize shall remove 'junit', but not 'api'. - * Unused classes of 'api' and theirs dependencies also shouldn't be removed. - */ - def 'use minimize with dependencies with api scope'() { - given: - file('settings.gradle') << """ - include 'api', 'lib', 'impl' - """.stripIndent() - - file('lib/src/main/java/lib/LibEntity.java') << """ - package lib; - public interface LibEntity {} - """.stripIndent() - - file('lib/src/main/java/lib/UnusedLibEntity.java') << """ - package lib; - public class UnusedLibEntity implements LibEntity {} - """.stripIndent() - - file('lib/build.gradle') << """ - apply plugin: 'java' - - """.stripIndent() - - file('api/src/main/java/api/Entity.java') << """ - package api; - public interface Entity {} - """.stripIndent() - - file('api/src/main/java/api/UnusedEntity.java') << """ - package api; - import lib.LibEntity; - public class UnusedEntity implements LibEntity {} - """.stripIndent() - - file('api/build.gradle') << """ - apply plugin: 'java' - - dependencies { - implementation 'junit:junit:3.8.2' - implementation project(':lib') - } - """.stripIndent() - - file('impl/src/main/java/impl/SimpleEntity.java') << """ - package impl; - import api.Entity; - public class SimpleEntity implements Entity {} - """.stripIndent() - - file('impl/build.gradle') << """ - ${getDefaultProjectBuildScript('java-library')} - - $shadowJar { - minimize() - } - - dependencies { api project(':api') } - """.stripIndent() - - File serverOutput = file('impl/build/libs/impl-all.jar') - - when: - run(':impl:shadowJar') - - then: - serverOutput.exists() - assertContains(serverOutput, [ - 'impl/SimpleEntity.class', - 'api/Entity.class', - 'api/UnusedEntity.class', - 'lib/LibEntity.class', - ]) - assertDoesNotContain(serverOutput, ['junit/framework/Test.class', 'lib/UnusedLibEntity.class']) - } - - /** - * 'api' used as api for 'impl', and 'lib' used as api for 'api'. - * Unused classes of 'api' and 'lib' shouldn't be removed. - */ - def 'use minimize with transitive dependencies with api scope'() { - given: - file('settings.gradle') << """ - include 'api', 'lib', 'impl' - """.stripIndent() - - file('lib/src/main/java/lib/LibEntity.java') << """ - package lib; - public interface LibEntity {} - """.stripIndent() - - file('lib/src/main/java/lib/UnusedLibEntity.java') << """ - package lib; - public class UnusedLibEntity implements LibEntity {} - """.stripIndent() - - file('lib/build.gradle') << """ - apply plugin: 'java' - - """.stripIndent() - - file('api/src/main/java/api/Entity.java') << """ - package api; - public interface Entity {} - """.stripIndent() - - file('api/src/main/java/api/UnusedEntity.java') << """ - package api; - import lib.LibEntity; - public class UnusedEntity implements LibEntity {} - """.stripIndent() - - file('api/build.gradle') << """ - apply plugin: 'java-library' - - dependencies { api project(':lib') } - """.stripIndent() - - file('impl/src/main/java/impl/SimpleEntity.java') << """ - package impl; - import api.Entity; - public class SimpleEntity implements Entity {} - """.stripIndent() - - file('impl/build.gradle') << """ - ${getDefaultProjectBuildScript('java-library')} - - $shadowJar { - minimize() - } - - dependencies { api project(':api') } - """.stripIndent() - - File serverOutput = file('impl/build/libs/impl-all.jar') - - when: - run(':impl:shadowJar') - - then: - serverOutput.exists() - assertContains(serverOutput, [ - 'impl/SimpleEntity.class', - 'api/Entity.class', - 'api/UnusedEntity.class', - 'lib/LibEntity.class', - 'lib/UnusedLibEntity.class' - ]) - } - - def 'depend on project shadow jar'() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - - file('client/build.gradle') << """ - $defaultProjectBuildScript - - dependencies { implementation 'junit:junit:3.8.2' } - - $shadowJar { - relocate 'junit.framework', 'client.junit.framework' - } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - - import client.Client; - import client.junit.framework.Test; - - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - apply plugin: 'java' - - dependencies { implementation project(path: ':client', configuration: 'shadow') } - """.stripIndent() - - File serverOutput = file('server/build/libs/server.jar') - - when: - run(':server:jar') - - then: - serverOutput.exists() - assertContains(serverOutput, [ - 'server/Server.class' - ]) - - and: - assertDoesNotContain(serverOutput, [ - 'client/Client.class', - 'junit/framework/Test.class', - 'client/junit/framework/Test.class' - ]) - } - - def 'shadow a project shadow jar'() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - - file('client/build.gradle') << """ - $defaultProjectBuildScript - - dependencies { implementation 'junit:junit:3.8.2' } - - $shadowJar { - relocate 'junit.framework', 'client.junit.framework' - } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - - import client.Client; - import client.junit.framework.Test; - - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - $defaultProjectBuildScript - - dependencies { implementation project(path: ':client', configuration: 'shadow') } - """.stripIndent() - - File serverOutput = file('server/build/libs/server-all.jar') - - when: - run(':server:shadowJar') - - then: - serverOutput.exists() - assertContains(serverOutput, [ - 'client/Client.class', - 'client/junit/framework/Test.class', - 'server/Server.class', - ]) - - and: - assertDoesNotContain(serverOutput, [ - 'junit/framework/Test.class' - ]) - } - - def "exclude INDEX.LIST, *.SF, *.DSA, and *.RSA by default"() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('META-INF/INDEX.LIST', 'JarIndex-Version: 1.0') - .insertFile('META-INF/a.SF', 'Signature File') - .insertFile('META-INF/a.DSA', 'DSA Signature Block') - .insertFile('META-INF/a.RSA', 'RSA Signature Block') - .insertFile('META-INF/a.properties', 'key=value') - .publish() - - file('src/main/java/shadow/Passed.java') << ''' - package shadow; - public class Passed {} - '''.stripIndent() - - projectScriptFile << """ - dependencies { implementation 'shadow:a:1.0' } - """.stripIndent() - - when: - run('shadowJar') - - then: - assertContains(outputShadowJar, ['a.properties', 'META-INF/a.properties']) - - and: - assertDoesNotContain(outputShadowJar, ['META-INF/INDEX.LIST', 'META-INF/a.SF', 'META-INF/a.DSA', 'META-INF/a.RSA']) - } - - def "include runtime configuration by default"() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .publish() - - repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() - - projectScriptFile << """ - dependencies { - runtimeOnly 'shadow:a:1.0' - shadow 'shadow:b:1.0' - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assertContains(outputShadowJar, ['a.properties']) - - and: - assertDoesNotContain(outputShadowJar, ['b.properties']) - } - - def "include java-library configurations by default"() { - given: - repo.module('shadow', 'api', '1.0') - .insertFile('api.properties', 'api') - .publish() - - repo.module('shadow', 'implementation-dep', '1.0') - .insertFile('implementation-dep.properties', 'implementation-dep') - .publish() - - repo.module('shadow', 'implementation', '1.0') - .insertFile('implementation.properties', 'implementation') - .dependsOn('implementation-dep') - .publish() - - repo.module('shadow', 'runtimeOnly', '1.0') - .insertFile('runtimeOnly.properties', 'runtimeOnly') - .publish() - - projectScriptFile.text = getDefaultProjectBuildScript('java-library', true, true) - projectScriptFile << """ - dependencies { - api 'shadow:api:1.0' - implementation 'shadow:implementation:1.0' - runtimeOnly 'shadow:runtimeOnly:1.0' - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assertContains(outputShadowJar, ['api.properties', 'implementation.properties', - 'runtimeOnly.properties', 'implementation-dep.properties']) - } - - def "doesn't include compileOnly configuration by default"() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .publish() - - repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() - - projectScriptFile << """ - dependencies { - runtimeOnly 'shadow:a:1.0' - compileOnly 'shadow:b:1.0' - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assertContains(outputShadowJar, ['a.properties']) - - and: - assertDoesNotContain(outputShadowJar, ['b.properties']) - } - - def "default copying strategy"() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('META-INF/MANIFEST.MF', 'MANIFEST A') - .publish() - - repo.module('shadow', 'b', '1.0') - .insertFile('META-INF/MANIFEST.MF', 'MANIFEST B') - .publish() - - projectScriptFile << """ - dependencies { - runtimeOnly 'shadow:a:1.0' - runtimeOnly 'shadow:b:1.0' - } - """.stripIndent() - - when: - run('shadowJar') - - then: - JarFile jar = new JarFile(outputShadowJar) - assert jar.entries().collect().size() == 2 - } - - def "Class-Path in Manifest not added if empty"() { - given: - - projectScriptFile << """ - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - JarFile jar = new JarFile(outputShadowJar) - Attributes attributes = jar.manifest.getMainAttributes() - assert attributes.getValue('Class-Path') == null - } - - @Issue('https://github.com/GradleUp/shadow/issues/65') - def "add shadow configuration to Class-Path in Manifest"() { - given: - - projectScriptFile << """ - dependencies { - shadow 'junit:junit:3.8.2' - } - - jar { - manifest { - attributes 'Class-Path': '/libs/a.jar' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: 'https://github.com/GradleUp/shadow/issues/65 - combine w/ existing Class-Path' - JarFile jar = new JarFile(outputShadowJar) - Attributes attributes = jar.manifest.getMainAttributes() - String classpath = attributes.getValue('Class-Path') - assert classpath == '/libs/a.jar junit-3.8.2.jar' - - } - - @Issue('https://github.com/GradleUp/shadow/issues/92') - def "do not include null value in Class-Path when jar file does not contain Class-Path"() { - given: - - projectScriptFile << """ - dependencies { shadow 'junit:junit:3.8.2' } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - JarFile jar = new JarFile(outputShadowJar) - Attributes attributes = jar.manifest.getMainAttributes() - String classpath = attributes.getValue('Class-Path') - assert classpath == 'junit-3.8.2.jar' - - } - - @Issue('https://github.com/GradleUp/shadow/issues/203') - def "support ZipCompression.STORED"() { - given: - - projectScriptFile << """ - dependencies { shadow 'junit:junit:3.8.2' } - - $shadowJar { - zip64 = true - entryCompression = org.gradle.api.tasks.bundling.ZipEntryCompression.STORED - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - } - - def 'api project dependency with version'() { - given: - file('settings.gradle') << """ - include 'api', 'lib', 'impl' - """.stripIndent() - - file('lib/build.gradle') << """ - apply plugin: 'java' - version = '1.0' - - """.stripIndent() - - file('api/src/main/java/api/UnusedEntity.java') << """ - package api; - public class UnusedEntity {} - """.stripIndent() - - file('api/build.gradle') << """ - apply plugin: 'java' - version = '1.0' - - dependencies { - implementation 'junit:junit:3.8.2' - implementation project(':lib') - } - """.stripIndent() - - file('impl/build.gradle') << """ - ${getDefaultProjectBuildScript('java-library')} - - version = '1.0' - - dependencies { api project(':api') } - - shadowJar.minimize() - """.stripIndent() - - File serverOutput = file('impl/build/libs/impl-1.0-all.jar') - - when: - run(':impl:shadowJar') - - then: - serverOutput.exists() - assertContains(serverOutput, [ - 'api/UnusedEntity.class', - ]) - } - - @Issue('https://github.com/GradleUp/shadow/issues/143') - @Ignore("This spec requires > 15 minutes and > 8GB of disk space to run") - def "check large zip files with zip64 enabled"() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - - file('src/main/java/myapp/Main.java') << """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.stripIndent() - - projectScriptFile << """ - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - - dependencies { - implementation 'shadow:a:1.0' - } - - def generatedResourcesDir = new File(project.layout.buildDirectory.asFile.get(), "generated-resources") - - task generateResources { - doLast { - def rnd = new Random() - def buf = new byte[128 * 1024] - for (x in 0..255) { - def dir = new File(generatedResourcesDir, x.toString()) - dir.mkdirs() - for (y in 0..255) { - def file = new File(dir, y.toString()) - rnd.nextBytes(buf) - file.bytes = buf - } - } - } - } - - sourceSets { - main { - output.dir(generatedResourcesDir, builtBy: generateResources) - } - } - - $shadowJar { - zip64 = true - } - - runShadow { - args 'foo' - } - """.stripIndent() - - settingsScriptFile << "rootProject.name = 'myapp'" - - when: - BuildResult result = run('runShadow') - - then: 'tests that runShadow executed and exited' - assert result.output.contains('TestApp: Hello World! (foo)') - - - } - - @Issue("https://github.com/GradleUp/shadow/issues/609") - def "doesn't error when using application mainClass property"() { - given: - projectScriptFile.text = getDefaultProjectBuildScript() - - projectScriptFile << """ - project.ext { - aspectjVersion = '1.8.12' - } - - apply plugin: 'application' - - application { - mainClass.set('myapp.Main') - } - - runShadow { - args 'foo' - } - - """ - - file('src/main/java/myapp/Main.java') << """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.stripIndent() - - when: - BuildResult result = run('runShadow') - - then: 'tests that runShadow executed and exited' - assert result.output.contains('TestApp: Hello World! (foo)') - } - - @Issue("https://github.com/GradleUp/shadow/pull/459") - def 'exclude gradleApi() by default'() { - given: - projectScriptFile.text = getDefaultProjectBuildScript('java-gradle-plugin', true, true) - - file('src/main/java/my/plugin/MyPlugin.java') << """ - package my.plugin; - import org.gradle.api.Plugin; - import org.gradle.api.Project; - public class MyPlugin implements Plugin { - public void apply(Project project) { - System.out.println("MyPlugin: Hello World!"); - } - } - """.stripIndent() - file('src/main/resources/META-INF/gradle-plugins/my.plugin.properties') << """ - implementation-class=my.plugin.MyPlugin - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - JarFile jar = new JarFile(outputShadowJar) - assert jar.entries().collect().findAll { it.name.endsWith('.class') }.size() == 1 - } - - @Issue("https://github.com/GradleUp/shadow/issues/1070") - def 'can register a custom shadow jar task'() { - projectScriptFile << """ - dependencies { - testImplementation 'junit:junit:3.8.2' - } - - def testShadowJar = tasks.register('testShadowJar', ${ShadowJar.name}) { - group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME - description = "Create a combined JAR of project and test dependencies" - - archiveClassifier = "tests" - from sourceSets.test.output - configurations = [project.configurations.testRuntimeClasspath] - } - """.stripIndent() - - when: - def result = run('testShadowJar') - - then: - assert result.task(":testShadowJar").outcome == TaskOutcome.SUCCESS - - and: - def jarFile = new JarFile(file("build/libs/shadow-1.0-tests.jar")) - assert jarFile.getEntry('junit') != null - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index 7f047228b..d240c49fe 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -114,6 +114,7 @@ abstract class BasePluginTest { open val shadowJarTask = SHADOW_JAR_TASK_NAME open val runShadowTask = SHADOW_RUN_TASK_NAME + val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" val projectScriptPath: Path get() = path("build.gradle") @@ -124,6 +125,9 @@ abstract class BasePluginTest { val outputShadowJar: Path get() = path("build/libs/shadow-1.0-all.jar") + val outputServerShadowJar: Path + get() = path("server/build/libs/server-1.0-all.jar") + fun path(path: String): Path { return root.resolve(path).also { val extension = it.extension @@ -173,17 +177,18 @@ abstract class BasePluginTest { return runner.withArguments(allArguments) } - fun run(vararg tasks: String): BuildResult { - return run(tasks.toList()) + inline fun run( + vararg tasks: String, + runnerBlock: (GradleRunner) -> GradleRunner = { it }, + ): BuildResult { + return runnerBlock(runner(tasks.toList())).build().assertNoDeprecationWarnings() } - inline fun run( - tasks: Iterable, - block: (GradleRunner) -> GradleRunner = { it }, + inline fun runWithFailure( + vararg tasks: String, + runnerBlock: (GradleRunner) -> GradleRunner = { it }, ): BuildResult { - return block(runner(tasks)).build().also { - it.assertNoDeprecationWarnings() - } + return runnerBlock(runner(tasks.toList())).buildAndFail().assertNoDeprecationWarnings() } fun writeClientAndServerModules( @@ -204,9 +209,11 @@ abstract class BasePluginTest { ) path("client/build.gradle").writeText( """ - ${getDefaultProjectBuildScript("java", withVersion = true)} - dependencies { implementation 'junit:junit:3.8.2' } - """.trimIndent(), + ${getDefaultProjectBuildScript("java")} + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent() + System.lineSeparator(), ) path("server/src/main/java/server/Server.java").writeText( @@ -225,8 +232,81 @@ abstract class BasePluginTest { $shadowJar { $serverShadowBlock } + """.trimIndent() + System.lineSeparator(), + ) + } + + fun writeApiLibAndImplModules() { + settingsScriptPath.appendText( + """ + include 'api', 'lib', 'impl' + """.trimIndent() + System.lineSeparator(), + ) + projectScriptPath.writeText("") + + path("lib/src/main/java/lib/LibEntity.java").writeText( + """ + package lib; + public interface LibEntity {} + """.trimIndent(), + ) + path("lib/src/main/java/lib/UnusedLibEntity.java").writeText( + """ + package lib; + public class UnusedLibEntity implements LibEntity {} + """.trimIndent(), + ) + path("lib/build.gradle").writeText( + """ + plugins { + id 'java' + } + """.trimIndent() + System.lineSeparator(), + ) + + path("api/src/main/java/api/Entity.java").writeText( + """ + package api; + public interface Entity {} + """.trimIndent(), + ) + path("api/src/main/java/api/UnusedEntity.java").writeText( + """ + package api; + import lib.LibEntity; + public class UnusedEntity implements LibEntity {} """.trimIndent(), ) + path("api/build.gradle").writeText( + """ + plugins { + id 'java' + } + dependencies { + implementation 'junit:junit:3.8.2' + implementation project(':lib') + } + """.trimIndent() + System.lineSeparator(), + ) + + path("impl/src/main/java/impl/SimpleEntity.java").writeText( + """ + package impl; + import api.Entity; + public class SimpleEntity implements Entity {} + """.trimIndent(), + ) + path("impl/build.gradle").writeText( + """ + ${getDefaultProjectBuildScript("java-library")} + dependencies { + api project(':api') + } + $shadowJar { + minimize() + } + """.trimIndent() + System.lineSeparator(), + ) } companion object { @@ -248,7 +328,7 @@ abstract class BasePluginTest { tasks.named('$SHADOW_RUN_TASK_NAME', ${JavaJarExec::class.java.name}) """.trimIndent() - fun BuildResult.assertNoDeprecationWarnings() { + fun BuildResult.assertNoDeprecationWarnings() = apply { output.lines().forEach { assert(!containsDeprecationWarning(it)) } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt index 33fbcc040..05857c983 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt @@ -98,17 +98,16 @@ class ConfigurationCacheSpec : BasePluginTest() { ) run(shadowJarTask) - val output = path("server/build/libs/server-1.0-all.jar") - output.deleteExisting() + outputServerShadowJar.deleteExisting() val result = run(shadowJarTask) - assertThat(output).exists() + assertThat(outputServerShadowJar).exists() assertContains( - output, + outputServerShadowJar, listOf("server/Server.class", "junit/framework/Test.class"), ) assertDoesNotContain( - output, + outputServerShadowJar, listOf("client/Client.class"), ) result.assertCcReused() diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 1eead9284..08c2f4cf5 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -188,16 +188,15 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(":server:$shadowJarTask") - val serverOutput = path("server/build/libs/server-1.0-all.jar") + run(serverShadowJarTask) - assertThat(serverOutput).exists() + assertThat(outputServerShadowJar).exists() assertDoesNotContain( - serverOutput, + outputServerShadowJar, listOf("client/Client.class"), ) assertContains( - serverOutput, + outputServerShadowJar, listOf("server/Server.class", "junit/framework/Test.class"), ) } @@ -212,16 +211,15 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(":server:$shadowJarTask") - val serverOutput = path("server/build/libs/server-1.0-all.jar") + run(serverShadowJarTask) - assertThat(serverOutput).exists() + assertThat(outputServerShadowJar).exists() assertDoesNotContain( - serverOutput, + outputServerShadowJar, listOf("junit/framework/Test.class"), ) assertContains( - serverOutput, + outputServerShadowJar, listOf("client/Client.class", "server/Server.class"), ) } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt new file mode 100644 index 000000000..e29791cf0 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -0,0 +1,814 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.exists +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import assertk.assertions.isNull +import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import java.util.jar.JarFile +import kotlin.io.path.appendText +import kotlin.io.path.readText +import kotlin.io.path.writeText +import org.gradle.api.plugins.JavaPlugin +import org.gradle.testfixtures.ProjectBuilder +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledForJreRange +import org.junit.jupiter.api.condition.JRE + +class ShadowPluginTest : BasePluginTest() { + + @Test + fun applyPlugin() { + val projectName = "my-shadow" + val version = "1.0.0" + + val project = ProjectBuilder.builder().withName(projectName).build().also { + it.version = version + } + project.plugins.apply(ShadowPlugin::class.java) + + assertThat(project.plugins.hasPlugin(ShadowPlugin::class.java)).isTrue() + assertThat(project.plugins.hasPlugin(LegacyShadowPlugin::class.java)).isTrue() + assertThat(project.tasks.findByName(shadowJarTask)).isNull() + + project.plugins.apply(JavaPlugin::class.java) + val shadowTask = project.tasks.getByName(shadowJarTask) as ShadowJar + val shadowConfig = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) + + assertThat(shadowTask.archiveBaseName.get()).isEqualTo(projectName) + assertThat(shadowTask.destinationDirectory.get().asFile) + .isEqualTo(project.layout.buildDirectory.dir("libs").get().asFile) + assertThat(shadowTask.archiveVersion.get()).isEqualTo(version) + assertThat(shadowTask.archiveClassifier.get()).isEqualTo("all") + assertThat(shadowTask.archiveExtension.get()).isEqualTo("jar") + assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile) + } + + @Test + @EnabledForJreRange( + max = JRE.JAVA_21, + disabledReason = "Gradle 8.3 doesn't support Java 21.", + ) + fun compatibleWithMinGradleVersion() { + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent(), + ) + + run(shadowJarTask) { + it.withGradleVersion("8.3") + } + + assertThat(outputShadowJar).exists() + } + + @Test + fun incompatibleWithLowerMinGradleVersion() { + runWithFailure(shadowJarTask) { + it.withGradleVersion("8.2") + } + } + + @Test + fun shadowCopy() { + val artifactJarPath = requireNotNull(this::class.java.classLoader.getResource("test-artifact-1.0-SNAPSHOT.jar")) + .path + val projectJarPath = requireNotNull(this::class.java.classLoader.getResource("test-project-1.0-SNAPSHOT.jar")) + .path + + projectScriptPath.appendText( + """ + $shadowJar { + from('$artifactJarPath') + from('$projectJarPath') + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).exists() + } + + @Test + fun includeProjectSources() { + path("src/main/java/shadow/Passed.java").writeText( + """ + package shadow; + public class Passed {} + """.trimIndent(), + ) + + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + archiveBaseName = 'shadow' + archiveClassifier = '' + archiveVersion = '' + } + """.trimIndent(), + ) + + run(shadowJarTask) + val outputShadowJar = path("build/libs/shadow.jar") + + assertThat(outputShadowJar).exists() + assertContains( + outputShadowJar, + listOf("shadow/Passed.class", "junit/framework/Test.class"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("/"), + ) + } + + @Test + fun includeProjectDependencies() { + writeClientAndServerModules() + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).exists() + assertContains( + outputServerShadowJar, + listOf("client/Client.class", "server/Server.class", "junit/framework/Test.class"), + ) + } + + /** + * 'Server' depends on 'Client'. 'junit' is independent. + * The minimize shall remove 'junit'. + */ + @Test + fun minimizeByKeepingOnlyTransitiveDependencies() { + writeClientAndServerModules( + serverShadowBlock = """ + minimize() + """.trimIndent(), + ) + path("server/src/main/java/server/Server.java").writeText( + """ + package server; + import client.Client; + public class Server { + // This is to make sure that 'Client' is not removed. + private final String client = Client.class.getName(); + } + """.trimIndent(), + ) + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).exists() + assertContains( + outputServerShadowJar, + listOf("client/Client.class", "server/Server.class"), + ) + assertDoesNotContain( + outputServerShadowJar, + listOf("junit/framework/Test.class"), + ) + } + + /** + * 'Client', 'Server' and 'junit' are independent. + * 'junit' is excluded from the minimize step. + * The minimize step shall remove 'Client' but not 'junit'. + */ + @Test + fun excludeDependencyFromMinimize() { + writeClientAndServerModules( + serverShadowBlock = """ + minimize { + exclude(dependency('junit:junit:.*')) + } + """.trimIndent(), + ) + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).exists() + assertContains( + outputServerShadowJar, + listOf("server/Server.class", "junit/framework/Test.class"), + ) + assertDoesNotContain( + outputServerShadowJar, + listOf("client/Client.class"), + ) + } + + /** + * 'Client', 'Server' and 'junit' are independent. + * Unused classes of 'client' and theirs dependencies shouldn't be removed. + */ + @Test + fun excludeProjectFromMinimize() { + writeClientAndServerModules( + serverShadowBlock = """ + minimize { + exclude(project(':client')) + } + """.trimIndent(), + ) + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).exists() + assertContains( + outputServerShadowJar, + listOf("client/Client.class", "server/Server.class"), + ) + } + + /** + * 'Client', 'Server' and 'junit' are independent. + * Unused classes of 'client' and theirs dependencies shouldn't be removed. + */ + @Test + fun excludeProjectFromMinimizeShallNotExcludeTransitiveDependenciesThatAreUsedInSubproject() { + writeClientAndServerModules( + serverShadowBlock = """ + minimize { + exclude(project(':client')) + } + """.trimIndent(), + ) + path("client/src/main/java/client/Client.java").writeText( + """ + package client; + import junit.framework.TestCase; + public class Client extends TestCase { + public static void main(String[] args) {} + } + """.trimIndent(), + ) + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).exists() + assertContains( + outputServerShadowJar, + listOf("client/Client.class", "server/Server.class", "junit/framework/TestCase.class"), + ) + + path("client/src/main/java/client/Client.java").writeText( + """ + package client; + public class Client {} + """.trimIndent(), + ) + run(serverShadowJarTask) + assertThat(outputServerShadowJar).exists() + // TODO: I don't think junit classes should be in the output jar, but it's the test case + // from https://github.com/GradleUp/shadow/pull/420, need to investigate more... + assertContains( + outputServerShadowJar, + listOf("client/Client.class", "server/Server.class", "junit/framework/TestCase.class"), + ) + } + + /** + * 'api' used as api for 'impl', and depended on 'lib'. 'junit' is independent. + * The minimize step shall remove 'junit', but not 'api'. + * Unused classes of 'api' and theirs dependencies also shouldn't be removed. + */ + @Test + fun useMinimizeWithDependenciesWithApiScope() { + writeApiLibAndImplModules() + + run(":impl:$shadowJarTask") + val implOutput = path("impl/build/libs/impl-all.jar") + + assertThat(implOutput).exists() + assertContains( + implOutput, + listOf("impl/SimpleEntity.class", "api/Entity.class", "api/UnusedEntity.class", "lib/LibEntity.class"), + ) + assertDoesNotContain( + implOutput, + listOf("junit/framework/Test.class", "lib/UnusedLibEntity.class"), + ) + } + + /** + * 'api' used as api for 'impl', and 'lib' used as api for 'api'. + * Unused classes of 'api' and 'lib' shouldn't be removed. + */ + @Test + fun useMinimizeWithTransitiveDependenciesWithApiScope() { + writeApiLibAndImplModules() + path("api/build.gradle").writeText( + """ + plugins { + id 'java-library' + } + dependencies { + api project(':lib') + } + """.trimIndent(), + ) + + run(":impl:$shadowJarTask") + val implOutput = path("impl/build/libs/impl-all.jar") + + assertThat(implOutput).exists() + assertContains( + implOutput, + listOf( + "impl/SimpleEntity.class", + "api/Entity.class", + "api/UnusedEntity.class", + "lib/LibEntity.class", + "lib/UnusedLibEntity.class", + ), + ) + assertDoesNotContain( + implOutput, + listOf("junit/framework/Test.class"), + ) + } + + @Test + fun dependOnProjectShadowJar() { + writeShadowedClientAndServerModules() + + run(":server:jar") + val serverOutput = path("server/build/libs/server-1.0.jar") + val clientOutput = path("client/build/libs/client-all.jar") + + assertThat(serverOutput).exists() + assertContains( + serverOutput, + listOf("server/Server.class"), + ) + assertDoesNotContain( + serverOutput, + listOf("client/Client.class", "junit/framework/Test.class", "client/junit/framework/Test.class"), + ) + assertThat(clientOutput).exists() + assertContains( + clientOutput, + listOf("client/Client.class", "client/junit/framework/Test.class"), + ) + } + + @Test + fun shadowProjectShadowJar() { + writeShadowedClientAndServerModules() + + run(serverShadowJarTask) + val clientOutput = path("client/build/libs/client-all.jar") + + assertThat(outputServerShadowJar).exists() + assertContains( + outputServerShadowJar, + listOf("client/Client.class", "client/junit/framework/Test.class", "server/Server.class"), + ) + assertDoesNotContain( + outputServerShadowJar, + listOf("junit/framework/Test.class"), + ) + assertThat(clientOutput).exists() + assertContains( + clientOutput, + listOf("client/Client.class", "client/junit/framework/Test.class"), + ) + } + + @Test + fun excludeSomeMetaInfFilesByDefault() { + repo.module("shadow", "a", "1.0") + .insertFile("a.properties", "a") + .insertFile("META-INF/INDEX.LIST", "JarIndex-Version: 1.0") + .insertFile("META-INF/a.SF", "Signature File") + .insertFile("META-INF/a.DSA", "DSA Signature Block") + .insertFile("META-INF/a.RSA", "RSA Signature Block") + .insertFile("META-INF/a.properties", "key=value") + .insertFile("module-info.class", "module myModuleName {}") + .publish() + + path("src/main/java/shadow/Passed.java").writeText( + """ + package shadow; + public class Passed {} + """.trimIndent(), + ) + projectScriptPath.appendText( + """ + dependencies { + implementation 'shadow:a:1.0' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf("shadow/Passed.class", "a.properties", "META-INF/a.properties"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("META-INF/INDEX.LIST", "META-INF/a.SF", "META-INF/a.DSA", "META-INF/a.RSA", "module-info.class"), + ) + } + + @Test + fun includeRuntimeConfigurationByDefault() { + publishArtifactA() + publishArtifactB() + + projectScriptPath.appendText( + """ + dependencies { + runtimeOnly 'shadow:a:1.0' + shadow 'shadow:b:1.0' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf("a.properties", "a2.properties"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("b.properties"), + ) + } + + @Test + fun includeJavaLibraryConfigurationsByDefault() { + repo.module("shadow", "api", "1.0") + .insertFile("api.properties", "api") + .publish() + repo.module("shadow", "implementation-dep", "1.0") + .insertFile("implementation-dep.properties", "implementation-dep") + .publish() + repo.module("shadow", "implementation", "1.0") + .insertFile("implementation.properties", "implementation") + .dependsOn("implementation-dep") + .publish() + repo.module("shadow", "runtimeOnly", "1.0") + .insertFile("runtimeOnly.properties", "runtimeOnly") + .publish() + + projectScriptPath.writeText( + """ + ${getDefaultProjectBuildScript("java-library", withGroup = true, withVersion = true)} + dependencies { + api 'shadow:api:1.0' + implementation 'shadow:implementation:1.0' + runtimeOnly 'shadow:runtimeOnly:1.0' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf("api.properties", "implementation.properties", "runtimeOnly.properties", "implementation-dep.properties"), + ) + } + + @Test + fun doesNotIncludeCompileOnlyConfigurationByDefault() { + publishArtifactA() + publishArtifactB() + + projectScriptPath.appendText( + """ + dependencies { + runtimeOnly 'shadow:a:1.0' + compileOnly 'shadow:b:1.0' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertContains( + outputShadowJar, + listOf("a.properties", "a2.properties"), + ) + assertDoesNotContain( + outputShadowJar, + listOf("b.properties"), + ) + } + + @Test + fun defaultCopyingStrategy() { + repo.module("shadow", "a", "1.0") + .insertFile("META-INF/MANIFEST.MF", "MANIFEST A") + .publish() + repo.module("shadow", "b", "1.0") + .insertFile("META-INF/MANIFEST.MF", "MANIFEST B") + .publish() + + projectScriptPath.appendText( + """ + dependencies { + runtimeOnly 'shadow:a:1.0' + runtimeOnly 'shadow:b:1.0' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).exists() + val entries = JarFile(outputShadowJar.toFile()).entries().toList() + assertThat(entries.size).isEqualTo(2) + } + + @Test + fun classPathInManifestNotAddedIfEmpty() { + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).exists() + val attributes = JarFile(outputShadowJar.toFile()).manifest.mainAttributes + assertThat(attributes.getValue("Class-Path")).isNull() + } + + /** + * https://github.com/GradleUp/shadow/issues/65 + */ + @Test + fun addShadowConfigurationToClassPathInManifest() { + projectScriptPath.appendText( + """ + dependencies { + shadow 'junit:junit:3.8.2' + } + jar { + manifest { + attributes 'Class-Path': '/libs/a.jar' + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).exists() + val attributes = JarFile(outputShadowJar.toFile()).manifest.mainAttributes + assertThat(attributes.getValue("Class-Path")).isEqualTo("/libs/a.jar junit-3.8.2.jar") + } + + /** + * https://github.com/GradleUp/shadow/issues/92 + */ + @Test + fun doNotIncludeNullValueInClassPathWhenJarFileDoesNotContainClassPath() { + projectScriptPath.appendText( + """ + dependencies { + shadow 'junit:junit:3.8.2' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).exists() + val attributes = JarFile(outputShadowJar.toFile()).manifest.mainAttributes + assertThat(attributes.getValue("Class-Path")).isEqualTo("junit-3.8.2.jar") + } + + /** + * https://github.com/GradleUp/shadow/issues/203 + */ + @Test + fun supportZipCompressionStored() { + projectScriptPath.appendText( + """ + dependencies { + shadow 'junit:junit:3.8.2' + } + $shadowJar { + zip64 = true + entryCompression = org.gradle.api.tasks.bundling.ZipEntryCompression.STORED + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).exists() + } + + /** + * This spec requires > 15 minutes and > 8GB of disk space to run + * + * https://github.com/GradleUp/shadow/issues/143 + */ + @Disabled + @Test + fun checkLargeZipFilesWithZip64Enabled() { + publishArtifactA() + + path("src/main/java/myapp/Main.java").writeText( + """ + package myapp; + public class Main { + public static void main(String[] args) { + System.out.println("TestApp: Hello World! (" + args[0] + ")"); + } + } + """.trimIndent(), + ) + + settingsScriptPath.appendText("rootProject.name = 'myapp'") + projectScriptPath.appendText( + """ + apply plugin: 'application' + + application { + mainClass = 'myapp.Main' + } + dependencies { + implementation 'shadow:a:1.0' + } + def generatedResourcesDir = new File(project.layout.buildDirectory.asFile.get(), "generated-resources") + def generateResources = tasks.register('generateResources') { + doLast { + def rnd = new Random() + def buf = new byte[128 * 1024] + for (x in 0..255) { + def dir = new File(generatedResourcesDir, x.toString()) + dir.mkdirs() + for (y in 0..255) { + def file = new File(dir, y.toString()) + rnd.nextBytes(buf) + file.bytes = buf + } + } + } + } + sourceSets { + main { + output.dir(generatedResourcesDir, builtBy: generateResources) + } + } + $shadowJar { + zip64 = true + } + $runShadow { + args 'foo' + } + """.trimIndent(), + ) + + val result = run(runShadowTask) + + assertThat(result.output).contains("TestApp: Hello World! (foo)") + } + + /** + * https://github.com/GradleUp/shadow/issues/609 + */ + @Test + fun doesNotErrorWhenUsingApplicationMainClassProperty() { + projectScriptPath.appendText( + """ + apply plugin: 'application' + + application { + mainClass = 'myapp.Main' + } + $runShadow { + args 'foo' + } + """.trimIndent(), + ) + + path("src/main/java/myapp/Main.java").writeText( + """ + package myapp; + public class Main { + public static void main(String[] args) { + System.out.println("TestApp: Hello World! (" + args[0] + ")"); + } + } + """.trimIndent(), + ) + + val result = run(runShadowTask) + + assertThat(result.output).contains("TestApp: Hello World! (foo)") + } + + /** + * https://github.com/GradleUp/shadow/pull/459 + */ + @Test + fun excludeGradleApiByDefault() { + projectScriptPath.writeText( + getDefaultProjectBuildScript("java-gradle-plugin", withGroup = true, withVersion = true), + ) + + path("src/main/java/my/plugin/MyPlugin.java").writeText( + """ + package my.plugin; + import org.gradle.api.Plugin; + import org.gradle.api.Project; + public class MyPlugin implements Plugin { + public void apply(Project project) { + System.out.println("MyPlugin: Hello World!"); + } + } + """.trimIndent(), + ) + path("src/main/resources/META-INF/gradle-plugins/my.plugin.properties").writeText( + """ + implementation-class=my.plugin.MyPlugin + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).exists() + + val entries = JarFile(outputShadowJar.toFile()).entries().toList() + assertThat(entries.count { it.name.endsWith(".class") }).isEqualTo(1) + } + + /** + * https://github.com/GradleUp/shadow/issues/1070 + */ + @Test + fun canRegisterCustomShadowJarTask() { + val testShadowJarTask = "testShadowJar" + projectScriptPath.appendText( + """ + dependencies { + testImplementation 'junit:junit:3.8.2' + } + def $testShadowJarTask = tasks.register('$testShadowJarTask', ${ShadowJar::class.java.name}) { + group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + description = "Create a combined JAR of project and test dependencies" + archiveClassifier = "tests" + from sourceSets.test.output + configurations = [project.configurations.testRuntimeClasspath] + } + """.trimIndent(), + ) + + val result = run(testShadowJarTask) + val testJar = path("build/libs/shadow-1.0-tests.jar") + + assertThat(result.task(":$testShadowJarTask")).isNotNull() + .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) + + assertThat(testJar).exists() + assertThat(JarFile(testJar.toFile()).getEntry("junit")).isNotNull() + } + + private fun writeShadowedClientAndServerModules() { + writeClientAndServerModules() + path("client/build.gradle").appendText( + """ + $shadowJar { + relocate 'junit.framework', 'client.junit.framework' + } + """.trimIndent(), + ) + path("server/src/main/java/server/Server.java").writeText( + """ + package server; + import client.Client; + import client.junit.framework.Test; + public class Server {} + """.trimIndent(), + ) + val replaced = path("server/build.gradle").readText() + .replace("project(':client')", "project(path: ':client', configuration: 'shadow')") + path("server/build.gradle").writeText(replaced) + } +} From 516f4054efc2e23ac788a4569d07eb3278f67373 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 4 Jan 2025 03:51:38 -0500 Subject: [PATCH 120/941] Port @Issue for Junit 5 (#1126) * Add Issue and IssueExtension * Replace issue links * Add ExtensionRegistryTests for IssueExtension * Enable junit.jupiter.extensions.autodetection.enabled for IssueExtension * Revert "Add ExtensionRegistryTests for IssueExtension" This reverts commit ff13d020 --- build.gradle.kts | 4 ++ .../gradle/plugins/shadow/RelocationTest.kt | 29 ++++++------- .../gradle/plugins/shadow/ShadowPluginTest.kt | 42 ++++++++++--------- .../gradle/plugins/shadow/util/Issue.kt | 31 ++++++++++++++ .../org.junit.jupiter.api.extension.Extension | 1 + 5 files changed, 73 insertions(+), 34 deletions(-) create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt create mode 100644 src/intiTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/build.gradle.kts b/build.gradle.kts index 4fd1f40de..2c951a4d1 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -106,6 +106,10 @@ val integrationTest by tasks.registering(Test::class) { testClassesDirs = intiTest.output.classesDirs classpath = intiTest.runtimeClasspath + // TODO: this should be moved into functionalTest after we migrated all functional tests to Kotlin. + // Required to enable `IssueExtension` for all tests. + systemProperty("junit.jupiter.extensions.autodetection.enabled", true) + val docsDir = file("src/docs") // Add src/docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. inputs.dir(docsDir) diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index a80671375..b0e002124 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -5,6 +5,7 @@ import assertk.assertThat import assertk.assertions.exists import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf +import com.github.jengelman.gradle.plugins.shadow.util.Issue import java.net.URLClassLoader import java.util.jar.JarFile import kotlin.io.path.appendText @@ -50,9 +51,9 @@ class RelocationTest : BasePluginTest() { ) } - /** - * https://github.com/GradleUp/shadow/issues/58 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/58", + ) @Test fun relocateDependencyFiles() { projectScriptPath.appendText( @@ -179,10 +180,10 @@ class RelocationTest : BasePluginTest() { ) } - /** - * https://github.com/GradleUp/shadow/issues/53 - * https://github.com/GradleUp/shadow/issues/55 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/53", + "https://github.com/GradleUp/shadow/issues/55", + ) @Test fun remapClassNamesForRelocatedFilesInProjectSource() { projectScriptPath.appendText( @@ -314,10 +315,10 @@ class RelocationTest : BasePluginTest() { ) } - /** - * https://github.com/GradleUp/shadow/issues/93 - * https://github.com/GradleUp/shadow/issues/114 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/93", + "https://github.com/GradleUp/shadow/issues/114", + ) @Test fun relocateResourceFiles() { repo.module("shadow", "dep", "1.0") @@ -363,9 +364,9 @@ class RelocationTest : BasePluginTest() { ) } - /** - * https://github.com/GradleUp/shadow/issues/294 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/294", + ) @Test fun doesNotErrorOnRelocatingJava9Classes() { projectScriptPath.appendText( diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt index e29791cf0..7aa7674ff 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -9,6 +9,7 @@ import assertk.assertions.isNull import assertk.assertions.isTrue import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.util.Issue import java.util.jar.JarFile import kotlin.io.path.appendText import kotlin.io.path.readText @@ -556,9 +557,9 @@ class ShadowPluginTest : BasePluginTest() { assertThat(attributes.getValue("Class-Path")).isNull() } - /** - * https://github.com/GradleUp/shadow/issues/65 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/65", + ) @Test fun addShadowConfigurationToClassPathInManifest() { projectScriptPath.appendText( @@ -581,9 +582,9 @@ class ShadowPluginTest : BasePluginTest() { assertThat(attributes.getValue("Class-Path")).isEqualTo("/libs/a.jar junit-3.8.2.jar") } - /** - * https://github.com/GradleUp/shadow/issues/92 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/92", + ) @Test fun doNotIncludeNullValueInClassPathWhenJarFileDoesNotContainClassPath() { projectScriptPath.appendText( @@ -601,9 +602,9 @@ class ShadowPluginTest : BasePluginTest() { assertThat(attributes.getValue("Class-Path")).isEqualTo("junit-3.8.2.jar") } - /** - * https://github.com/GradleUp/shadow/issues/203 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/203", + ) @Test fun supportZipCompressionStored() { projectScriptPath.appendText( @@ -625,9 +626,10 @@ class ShadowPluginTest : BasePluginTest() { /** * This spec requires > 15 minutes and > 8GB of disk space to run - * - * https://github.com/GradleUp/shadow/issues/143 */ + @Issue( + "https://github.com/GradleUp/shadow/issues/143", + ) @Disabled @Test fun checkLargeZipFilesWithZip64Enabled() { @@ -690,9 +692,9 @@ class ShadowPluginTest : BasePluginTest() { assertThat(result.output).contains("TestApp: Hello World! (foo)") } - /** - * https://github.com/GradleUp/shadow/issues/609 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/609", + ) @Test fun doesNotErrorWhenUsingApplicationMainClassProperty() { projectScriptPath.appendText( @@ -724,9 +726,9 @@ class ShadowPluginTest : BasePluginTest() { assertThat(result.output).contains("TestApp: Hello World! (foo)") } - /** - * https://github.com/GradleUp/shadow/pull/459 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/459", + ) @Test fun excludeGradleApiByDefault() { projectScriptPath.writeText( @@ -759,9 +761,9 @@ class ShadowPluginTest : BasePluginTest() { assertThat(entries.count { it.name.endsWith(".class") }).isEqualTo(1) } - /** - * https://github.com/GradleUp/shadow/issues/1070 - */ + @Issue( + "https://github.com/GradleUp/shadow/issues/1070", + ) @Test fun canRegisterCustomShadowJarTask() { val testShadowJarTask = "testShadowJar" diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt new file mode 100644 index 000000000..e7b936358 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt @@ -0,0 +1,31 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import org.junit.jupiter.api.Tags +import org.junit.jupiter.api.extension.BeforeEachCallback +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.ExtensionContext.Namespace + +/** + * This is related to [spock.lang.Issue](https://github.com/spockframework/spock/blob/master/spock-core/src/main/java/spock/lang/Issue.java) but is used for JUnit 5 tests. + * + * @see [Tags] + */ +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +annotation class Issue(vararg val values: String) + +class IssueExtension : BeforeEachCallback { + override fun beforeEach(context: ExtensionContext) { + val issueAnnotation = context.requiredTestMethod.getAnnotation(Issue::class.java) ?: return + val store = context.getStore(Namespace.create(IssueExtension::class.java, context.requiredTestClass)) + store.put("tags", issueAnnotation.values.map(::issueLinkToTag)) + } +} + +private fun issueLinkToTag(link: String): String { + return issueLinkRegex.replace(link) { matchResult -> + "ISSUE-${matchResult.groupValues[1]}" + } +} + +private val issueLinkRegex = "https://github\\.com/[^/]+/[^/]+/issues/(\\d+)".toRegex() diff --git a/src/intiTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/src/intiTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 000000000..fd3002933 --- /dev/null +++ b/src/intiTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +com.github.jengelman.gradle.plugins.shadow.util.IssueExtension From 7500787c12f25b8d60ce13f99666098e3eb20713 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 4 Jan 2025 10:34:44 -0500 Subject: [PATCH 121/941] Merge duplicate checks in RelocationTest (#1128) --- .../gradle/plugins/shadow/RelocationTest.kt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index b0e002124..100d4e323 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -152,6 +152,12 @@ class RelocationTest : BasePluginTest() { "b/TestResult.class", "b/TestSuite\$1.class", "b/TestSuite.class", + "junit/textui/TestRunner.class", + "junit/framework/Assert.class", + "junit/framework/AssertionFailedError.class", + "junit/framework/ComparisonCompactor.class", + "junit/framework/ComparisonFailure.class", + "junit/framework/Protectable.class", ), ) @@ -166,18 +172,6 @@ class RelocationTest : BasePluginTest() { "b/Protectable.class", ), ) - - assertContains( - outputShadowJar, - listOf( - "junit/textui/TestRunner.class", - "junit/framework/Assert.class", - "junit/framework/AssertionFailedError.class", - "junit/framework/ComparisonCompactor.class", - "junit/framework/ComparisonFailure.class", - "junit/framework/Protectable.class", - ), - ) } @Issue( From dcb6620f7b3534c384eef56f4110440603740dd1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 5 Jan 2025 02:27:31 -0500 Subject: [PATCH 122/941] Public const values of GroovyExtensionModuleTransformer (#1129) --- api/shadow.api | 12 ++++++++ .../GroovyExtensionModuleTransformer.kt | 28 +++++++------------ 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index e9bb98b26..bbee96bb6 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -434,6 +434,15 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/DontInclude } public class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer$Companion; + public static final field EXTENSION_CLASSES_KEY Ljava/lang/String; + public static final field GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH Ljava/lang/String; + public static final field GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH Ljava/lang/String; + public static final field MERGED_MODULE_NAME Ljava/lang/String; + public static final field MERGED_MODULE_VERSION Ljava/lang/String; + public static final field MODULE_NAME_KEY Ljava/lang/String; + public static final field MODULE_VERSION_KEY Ljava/lang/String; + public static final field STATIC_EXTENSION_CLASSES_KEY Ljava/lang/String; public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun hasTransformedResource ()Z @@ -441,6 +450,9 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExten public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } +public final class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer$Companion { +} + public class com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index d0884e210..3cfba5754 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -1,9 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.inputStream import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.InputStream import java.util.Properties import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream @@ -88,22 +86,16 @@ public open class GroovyExtensionModuleTransformer : Transformer { os.closeEntry() } - private companion object { - private fun Properties.inputStream(): InputStream { - val os = ByteArrayOutputStream() - store(os, null) - return ByteArrayInputStream(os.toByteArray()) - } - - private const val GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH = + public companion object { + public const val GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH: String = "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" - private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH = + public const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH: String = "META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule" - private const val MODULE_NAME_KEY = "moduleName" - private const val MODULE_VERSION_KEY = "moduleVersion" - private const val EXTENSION_CLASSES_KEY = "extensionClasses" - private const val STATIC_EXTENSION_CLASSES_KEY = "staticExtensionClasses" - private const val MERGED_MODULE_NAME = "MergedByShadowJar" - private const val MERGED_MODULE_VERSION = "1.0.0" + public const val MODULE_NAME_KEY: String = "moduleName" + public const val MODULE_VERSION_KEY: String = "moduleVersion" + public const val EXTENSION_CLASSES_KEY: String = "extensionClasses" + public const val STATIC_EXTENSION_CLASSES_KEY: String = "staticExtensionClasses" + public const val MERGED_MODULE_NAME: String = "MergedByShadowJar" + public const val MERGED_MODULE_VERSION: String = "1.0.0" } } From 18d659e6b2453c2129cbfe1d6befff86c60ab6dc Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 5 Jan 2025 03:01:53 -0500 Subject: [PATCH 123/941] Migrate functional tests to Kotlin and Junit part 3 (#1125) * 1/2 of TransformerSpec * 2/2 of TransformerSpec * Refine TransformersTest * Split BaseTransformerTest * Split ServiceFileTransformerTest * Cleanups * Add `JarPath` to simplify assertions * Split AppendingTransformerTest * Split GroovyExtensionModuleTransformerTest * Rename transformerShouldNotHaveDeprecatedBehaviours to otherTransformers * Cleanups --- .../plugins/shadow/TransformerSpec.groovy | 746 ------------------ .../gradle/plugins/shadow/ApplicationTest.kt | 19 +- .../gradle/plugins/shadow/BasePluginTest.kt | 49 +- .../plugins/shadow/ConfigurationCacheSpec.kt | 27 +- .../gradle/plugins/shadow/FilteringTest.kt | 109 +-- .../gradle/plugins/shadow/RelocationTest.kt | 233 +++--- .../gradle/plugins/shadow/ShadowPluginTest.kt | 260 +++--- .../transformers/AppendingTransformerTest.kt | 54 ++ .../transformers/BaseTransformerTest.kt | 70 ++ .../GroovyExtensionModuleTransformerTest.kt | 110 +++ .../ServiceFileTransformerTest.kt | 203 +++++ .../shadow/transformers/TransformersTest.kt | 166 ++++ .../gradle/plugins/shadow/util/JarPath.kt | 47 ++ 13 files changed, 972 insertions(+), 1121 deletions(-) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy deleted file mode 100644 index f9c5d3cd8..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/TransformerSpec.groovy +++ /dev/null @@ -1,746 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer -import spock.lang.Issue -import spock.lang.Unroll - -import java.util.jar.JarInputStream -import java.util.jar.Manifest - -class TransformerSpec extends BasePluginSpecification { - - def 'service resource transformer'() { - given: - def one = buildJar('one.jar') - .insert('META-INF/services/org.apache.maven.Shade', - 'one # NOTE: No newline terminates this line/file') - .insert('META-INF/services/com.acme.Foo', 'one') - .write() - - def two = buildJar('two.jar') - .insert('META-INF/services/org.apache.maven.Shade', - 'two # NOTE: No newline terminates this line/file') - .insert('META-INF/services/com.acme.Foo', 'two') - .write() - - projectScriptFile << """ - import ${ServiceFileTransformer.name} - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - transform(ServiceFileTransformer) { - exclude 'META-INF/services/com.acme.*' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - String text1 = getJarFileContents(outputShadowJar, 'META-INF/services/org.apache.maven.Shade') - assert text1.split("\\r?\\n").size() == 2 - assert text1 == - '''one # NOTE: No newline terminates this line/file -two # NOTE: No newline terminates this line/file'''.stripIndent() - - and: - String text2 = getJarFileContents(outputShadowJar, 'META-INF/services/com.acme.Foo') - assert text2.split("\\r?\\n").size() == 1 - assert text2 == 'one' - } - - def 'service resource transformer alternate path'() { - given: - def one = buildJar('one.jar').insert('META-INF/foo/org.apache.maven.Shade', - 'one # NOTE: No newline terminates this line/file').write() - - def two = buildJar('two.jar').insert('META-INF/foo/org.apache.maven.Shade', - 'two # NOTE: No newline terminates this line/file').write() - - projectScriptFile << """ - import ${ServiceFileTransformer.name} - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - transform(ServiceFileTransformer) { - path = 'META-INF/foo' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - String text = getJarFileContents(outputShadowJar, 'META-INF/foo/org.apache.maven.Shade') - assert text.split("\\r?\\n").size() == 2 - assert text == - '''one # NOTE: No newline terminates this line/file -two # NOTE: No newline terminates this line/file'''.stripIndent() - } - - def 'service resource transformer short syntax'() { - given: - def one = buildJar('one.jar') - .insert('META-INF/services/org.apache.maven.Shade', - 'one # NOTE: No newline terminates this line/file') - .insert('META-INF/services/com.acme.Foo', 'one') - .write() - - def two = buildJar('two.jar') - .insert('META-INF/services/org.apache.maven.Shade', - 'two # NOTE: No newline terminates this line/file') - .insert('META-INF/services/com.acme.Foo', 'two') - .write() - - projectScriptFile << """ - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - mergeServiceFiles { - exclude 'META-INF/services/com.acme.*' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - String text1 = getJarFileContents(outputShadowJar, 'META-INF/services/org.apache.maven.Shade') - assert text1.split("\\r?\\n").size() == 2 - assert text1 == - '''one # NOTE: No newline terminates this line/file -two # NOTE: No newline terminates this line/file'''.stripIndent() - - and: - String text2 = getJarFileContents(outputShadowJar, 'META-INF/services/com.acme.Foo') - assert text2.split("\\r?\\n").size() == 1 - assert text2 == 'one' - } - - def 'service resource transformer short syntax relocation'() { - given: - def one = buildJar('one.jar') - .insert('META-INF/services/java.sql.Driver', - '''oracle.jdbc.OracleDriver -org.apache.hive.jdbc.HiveDriver'''.stripIndent()) - .insert('META-INF/services/org.apache.axis.components.compiler.Compiler', - 'org.apache.axis.components.compiler.Javac') - .insert('META-INF/services/org.apache.commons.logging.LogFactory', - 'org.apache.commons.logging.impl.LogFactoryImpl') - .write() - - def two = buildJar('two.jar') - .insert('META-INF/services/java.sql.Driver', - '''org.apache.derby.jdbc.AutoloadedDriver -com.mysql.jdbc.Driver'''.stripIndent()) - .insert('META-INF/services/org.apache.axis.components.compiler.Compiler', - 'org.apache.axis.components.compiler.Jikes') - .insert('META-INF/services/org.apache.commons.logging.LogFactory', - 'org.mortbay.log.Factory') - .write() - - projectScriptFile << """ - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - mergeServiceFiles() - relocate('org.apache', 'myapache') { - exclude 'org.apache.axis.components.compiler.Jikes' - exclude 'org.apache.commons.logging.LogFactory' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - String text1 = getJarFileContents(outputShadowJar, 'META-INF/services/java.sql.Driver') - assert text1.split("\\r?\\n").size() == 4 - assert text1 == - '''oracle.jdbc.OracleDriver -myapache.hive.jdbc.HiveDriver -myapache.derby.jdbc.AutoloadedDriver -com.mysql.jdbc.Driver'''.stripIndent() - - and: - String text2 = getJarFileContents(outputShadowJar, 'META-INF/services/myapache.axis.components.compiler.Compiler') - assert text2.split("\\r?\\n").size() == 2 - assert text2 == - '''myapache.axis.components.compiler.Javac -org.apache.axis.components.compiler.Jikes'''.stripIndent() - - and: - String text3 = getJarFileContents(outputShadowJar, 'META-INF/services/org.apache.commons.logging.LogFactory') - assert text3.split("\\r?\\n").size() == 2 - assert text3 == - '''myapache.commons.logging.impl.LogFactoryImpl -org.mortbay.log.Factory'''.stripIndent() - } - - def 'service resource transformer short syntax alternate path'() { - given: - def one = buildJar('one.jar').insert('META-INF/foo/org.apache.maven.Shade', - 'one # NOTE: No newline terminates this line/file').write() - - def two = buildJar('two.jar').insert('META-INF/foo/org.apache.maven.Shade', - 'two # NOTE: No newline terminates this line/file').write() - - projectScriptFile << """ - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - mergeServiceFiles('META-INF/foo') - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - String text = getJarFileContents(outputShadowJar, 'META-INF/foo/org.apache.maven.Shade') - assert text.split("\\r?\\n").size() == 2 - assert text == - '''one # NOTE: No newline terminates this line/file -two # NOTE: No newline terminates this line/file'''.stripIndent() - } - - @Issue([ - 'https://github.com/GradleUp/shadow/issues/70', - 'https://github.com/GradleUp/shadow/issues/-71', - ]) - def 'apply transformers to project resources'() { - given: - def one = buildJar('one.jar').insert('META-INF/services/shadow.Shadow', - 'one # NOTE: No newline terminates this line/file').write() - - repo.module('shadow', 'two', '1.0').insertFile('META-INF/services/shadow.Shadow', - 'two # NOTE: No newline terminates this line/file').publish() - - projectScriptFile << """ - dependencies { - implementation 'shadow:two:1.0' - implementation files('${escapedPath(one)}') - } - - $shadowJar { - mergeServiceFiles() - } - """.stripIndent() - - file('src/main/resources/META-INF/services/shadow.Shadow') << - 'three # NOTE: No newline terminates this line/file' - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - String text = getJarFileContents(outputShadowJar, 'META-INF/services/shadow.Shadow') - assert text.split("\\r?\\n").size() == 3 - assert text == - '''three # NOTE: No newline terminates this line/file -one # NOTE: No newline terminates this line/file -two # NOTE: No newline terminates this line/file'''.stripIndent() - } - - def 'appending transformer'() { - given: - def one = buildJar('one.jar').insert('test.properties', - 'one # NOTE: No newline terminates this line/file').write() - - def two = buildJar('two.jar').insert('test.properties', - 'two # NOTE: No newline terminates this line/file').write() - - projectScriptFile << """ - import ${AppendingTransformer.name} - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - transform(AppendingTransformer) { - resource = 'test.properties' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - String text = getJarFileContents(outputShadowJar, 'test.properties') - assert text.split("\\r?\\n").size() == 2 - assert text == - '''one # NOTE: No newline terminates this line/file -two # NOTE: No newline terminates this line/file -'''.stripIndent() - } - - def 'appending transformer short syntax'() { - given: - def one = buildJar('one.jar').insert('test.properties', - 'one # NOTE: No newline terminates this line/file').write() - - def two = buildJar('two.jar').insert('test.properties', - 'two # NOTE: No newline terminates this line/file').write() - - projectScriptFile << """ - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - append('test.properties') - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - String text = getJarFileContents(outputShadowJar, 'test.properties') - assert text.split("\\r?\\n").size() == 2 - assert text == - '''one # NOTE: No newline terminates this line/file -two # NOTE: No newline terminates this line/file -'''.stripIndent() - } - - def 'manifest retained'() { - given: - File main = file('src/main/java/shadow/Main.java') - main << ''' - package shadow; - - public class Main { - - public static void main(String[] args) { } - } - '''.stripIndent() - - projectScriptFile << """ - jar { - manifest { - attributes 'Main-Class': 'shadow.Main' - attributes 'Test-Entry': 'PASSED' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - JarInputStream jis = new JarInputStream(outputShadowJar.newInputStream()) - Manifest mf = jis.manifest - jis.close() - - assert mf - assert mf.mainAttributes.getValue('Test-Entry') == 'PASSED' - assert mf.mainAttributes.getValue('Main-Class') == 'shadow.Main' - } - - def 'manifest transformed'() { - given: - File main = file('src/main/java/shadow/Main.java') - main << ''' - package shadow; - - public class Main { - - public static void main(String[] args) { } - } - '''.stripIndent() - - projectScriptFile << """ - jar { - manifest { - attributes 'Main-Class': 'shadow.Main' - attributes 'Test-Entry': 'FAILED' - } - } - - $shadowJar { - manifest { - attributes 'Test-Entry': 'PASSED' - attributes 'New-Entry': 'NEW' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - JarInputStream jis = new JarInputStream(outputShadowJar.newInputStream()) - Manifest mf = jis.manifest - jis.close() - - assert mf - assert mf.mainAttributes.getValue('Test-Entry') == 'PASSED' - assert mf.mainAttributes.getValue('Main-Class') == 'shadow.Main' - assert mf.mainAttributes.getValue('New-Entry') == 'NEW' - } - - def 'append xml files'() { - given: - def xml1 = buildJar('xml1.jar').insert('properties.xml', - ''' - - - val1 - -'''.stripIndent() - ).write() - - def xml2 = buildJar('xml2.jar').insert('properties.xml', - ''' - - - val2 - -'''.stripIndent() - ).write() - - projectScriptFile << """ - import ${XmlAppendingTransformer.name} - - $shadowJar { - from('${escapedPath(xml1)}') - from('${escapedPath(xml2)}') - transform(XmlAppendingTransformer) { - resource = 'properties.xml' - } - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - String text = getJarFileContents(outputShadowJar, 'properties.xml') - assert text.replaceAll('\r\n', '\n') == - ''' - - - val1 - val2 - -'''.stripIndent() - } - - @Issue('https://github.com/GradleUp/shadow/issues/82') - def 'shadow.manifest leaks to jar.manifest'() { - given: - File main = file('src/main/java/shadow/Main.java') - main << ''' - package shadow; - - public class Main { - - public static void main(String[] args) { } - } - '''.stripIndent() - - projectScriptFile << """ - jar { - manifest { - attributes 'Main-Class': 'shadow.Main' - attributes 'Test-Entry': 'FAILED' - } - } - - $shadowJar { - manifest { - attributes 'Test-Entry': 'PASSED' - attributes 'New-Entry': 'NEW' - } - } - """.stripIndent() - - when: - run('jar', 'shadowJar') - - then: - File jar = file('build/libs/shadow-1.0.jar') - assert jar.exists() - assert outputShadowJar.exists() - - then: 'Check contents of Shadow jar manifest' - JarInputStream jis = new JarInputStream(outputShadowJar.newInputStream()) - Manifest mf = jis.manifest - - assert mf - assert mf.mainAttributes.getValue('Test-Entry') == 'PASSED' - assert mf.mainAttributes.getValue('Main-Class') == 'shadow.Main' - assert mf.mainAttributes.getValue('New-Entry') == 'NEW' - - then: 'Check contents of jar manifest' - JarInputStream jis2 = new JarInputStream(jar.newInputStream()) - Manifest mf2 = jis2.manifest - - assert mf2 - assert mf2.mainAttributes.getValue('Test-Entry') == 'FAILED' - assert mf2.mainAttributes.getValue('Main-Class') == 'shadow.Main' - assert !mf2.mainAttributes.getValue('New-Entry') - - cleanup: - jis?.close() - jis2?.close() - } - - @Issue('https://github.com/GradleUp/shadow/issues/82') - def 'shadow manifest leaks to jar manifest'() { - given: - File main = file('src/main/java/shadow/Main.java') - main << ''' - package shadow; - - public class Main { - - public static void main(String[] args) { } - } - '''.stripIndent() - - projectScriptFile << """ - jar { - manifest { - attributes 'Main-Class': 'shadow.Main' - attributes 'Test-Entry': 'FAILED' - } - } - - $shadowJar { - manifest { - attributes 'Test-Entry': 'PASSED' - attributes 'New-Entry': 'NEW' - } - } - """.stripIndent() - - when: - run('jar', 'shadowJar') - - then: - File jar = file('build/libs/shadow-1.0.jar') - assert jar.exists() - assert outputShadowJar.exists() - - then: 'Check contents of Shadow jar manifest' - JarInputStream jis = new JarInputStream(outputShadowJar.newInputStream()) - Manifest mf = jis.manifest - - assert mf - assert mf.mainAttributes.getValue('Test-Entry') == 'PASSED' - assert mf.mainAttributes.getValue('Main-Class') == 'shadow.Main' - assert mf.mainAttributes.getValue('New-Entry') == 'NEW' - - then: 'Check contents of jar manifest' - JarInputStream jis2 = new JarInputStream(jar.newInputStream()) - Manifest mf2 = jis2.manifest - - assert mf2 - assert mf2.mainAttributes.getValue('Test-Entry') == 'FAILED' - assert mf2.mainAttributes.getValue('Main-Class') == 'shadow.Main' - assert !mf2.mainAttributes.getValue('New-Entry') - - cleanup: - jis?.close() - jis2?.close() - } - - def 'Groovy extension module transformer'() { - given: - def one = buildJar('one.jar') - .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', - '''moduleName=foo -moduleVersion=1.0.5 -extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension -staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() - - def two = buildJar('two.jar') - .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', - '''moduleName=bar -moduleVersion=2.3.5 -extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension -staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() - - projectScriptFile << """ - import ${GroovyExtensionModuleTransformer.name} - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - transform(GroovyExtensionModuleTransformer) - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - def text = getJarFileContents(outputShadowJar, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') - def props = new Properties() - props.load(new StringReader(text)) - assert props.getProperty('moduleName') == 'MergedByShadowJar' - assert props.getProperty('moduleVersion') == '1.0.0' - assert props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' - assert props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' - } - - def 'Groovy extension module transformer works for Groovy2_5+'() { - given: - def one = buildJar('one.jar') - .insert('META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule', - '''moduleName=foo -moduleVersion=1.0.5 -extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension -staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() - - def two = buildJar('two.jar') - .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', - '''moduleName=bar -moduleVersion=2.3.5 -extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension -staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() - - projectScriptFile << """ - import ${GroovyExtensionModuleTransformer.name} - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - transform(GroovyExtensionModuleTransformer) - } - """.stripIndent() - - when: - run('shadowJar') - - then: - outputShadowJar.exists() - - and: - def text = getJarFileContents(outputShadowJar, 'META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule') - def props = new Properties() - props.load(new StringReader(text)) - props.getProperty('moduleName') == 'MergedByShadowJar' - props.getProperty('moduleVersion') == '1.0.0' - props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' - props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' - assertDoesNotContain(outputShadowJar, ['META-INF/services/org.codehaus.groovy.runtime.ExtensionModule']) - } - - def 'Groovy extension module transformer short syntax'() { - given: - def one = buildJar('one.jar') - .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', - '''moduleName=foo -moduleVersion=1.0.5 -extensionClasses=com.acme.foo.FooExtension,com.acme.foo.BarExtension -staticExtensionClasses=com.acme.foo.FooStaticExtension'''.stripIndent()).write() - - def two = buildJar('two.jar') - .insert('META-INF/services/org.codehaus.groovy.runtime.ExtensionModule', - '''moduleName=bar -moduleVersion=2.3.5 -extensionClasses=com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension -staticExtensionClasses=com.acme.bar.SomeStaticExtension'''.stripIndent()).write() - - projectScriptFile << """ - $shadowJar { - from('${escapedPath(one)}') - from('${escapedPath(two)}') - mergeGroovyExtensionModules() - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - and: - def text = getJarFileContents(outputShadowJar, 'META-INF/services/org.codehaus.groovy.runtime.ExtensionModule') - def props = new Properties() - props.load(new StringReader(text)) - assert props.getProperty('moduleName') == 'MergedByShadowJar' - assert props.getProperty('moduleVersion') == '1.0.0' - assert props.getProperty('extensionClasses') == 'com.acme.foo.FooExtension,com.acme.foo.BarExtension,com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension' - assert props.getProperty('staticExtensionClasses') == 'com.acme.foo.FooStaticExtension,com.acme.bar.SomeStaticExtension' - } - - @Unroll - def '#transformer should not have deprecated behaviours'() { - given: - if (configuration.contains('test/some.file')) { - file('test/some.file') << 'some content' - } - projectScriptFile << """ - import com.github.jengelman.gradle.plugins.shadow.transformers.${transformer} - - $shadowJar { - transform(${transformer})${configuration} - } - """.stripIndent() - - when: - run('shadowJar') - - then: - assert outputShadowJar.exists() - - where: - transformer | configuration - 'ApacheLicenseResourceTransformer' | '' - 'ApacheNoticeResourceTransformer' | '' - 'AppendingTransformer' | '' - 'ComponentsXmlResourceTransformer' | '' - 'DontIncludeResourceTransformer' | '' - 'GroovyExtensionModuleTransformer' | '' - 'IncludeResourceTransformer' | '{ resource.set("test.file"); file.fileValue(file("test/some.file")) }' - 'Log4j2PluginsCacheFileTransformer' | '' - 'ManifestAppenderTransformer' | '' - 'ManifestResourceTransformer' | '' - 'PropertiesFileTransformer' | '{ keyTransformer = { it.toLowerCase() } }' - 'ServiceFileTransformer' | '' - 'XmlAppendingTransformer' | '' - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt index 6fe3f2464..fe67d30ea 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt @@ -5,7 +5,8 @@ import assertk.assertions.contains import assertk.assertions.containsAtLeast import assertk.assertions.exists import assertk.assertions.isEqualTo -import java.util.jar.JarFile +import assertk.assertions.isNotEmpty +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText @@ -39,13 +40,13 @@ class ApplicationTest : BasePluginTest() { assertThat(result.output).contains("Running application with JDK 17") assertThat(result.output).contains("TestApp: Hello World! (foo)") - val installedJar = path("build/install/myapp-shadow/lib/myapp-1.0-all.jar") - assertThat(installedJar).exists() - - assertContains(installedJar, listOf("a.properties", "a2.properties", "myapp/Main.class")) - - val jarFile = JarFile(installedJar.toFile()) - assertThat(jarFile.manifest.mainAttributes.getValue("Main-Class")) + val installedJar = jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar") + assertThat(installedJar).containsEntries( + "a.properties", + "a2.properties", + "myapp/Main.class", + ) + assertThat(installedJar.manifest.mainAttributes.getValue("Main-Class")) .isEqualTo("myapp.Main") path("build/install/myapp-shadow/bin/myapp").let { startScript -> @@ -84,7 +85,7 @@ class ApplicationTest : BasePluginTest() { run(ShadowApplicationPlugin.SHADOW_INSTALL_TASK_NAME) - assertThat(path("build/install/myapp-shadow/lib/myapp-1.0-all.jar")).exists() + assertThat(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar").entries().toList()).isNotEmpty() } private fun prepare( diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index d240c49fe..2a1319432 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -5,8 +5,9 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHA import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository +import com.github.jengelman.gradle.plugins.shadow.util.JarPath import java.nio.file.Path -import java.util.jar.JarFile +import java.util.Properties import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.Path import kotlin.io.path.absolutePathString @@ -16,7 +17,7 @@ import kotlin.io.path.createFile import kotlin.io.path.createTempDirectory import kotlin.io.path.deleteRecursively import kotlin.io.path.exists -import kotlin.io.path.extension +import kotlin.io.path.isRegularFile import kotlin.io.path.readText import kotlin.io.path.toPath import kotlin.io.path.writeText @@ -122,19 +123,25 @@ abstract class BasePluginTest { val settingsScriptPath: Path get() = path("settings.gradle") - val outputShadowJar: Path - get() = path("build/libs/shadow-1.0-all.jar") + val outputShadowJar: JarPath + get() = jarPath("build/libs/shadow-1.0-all.jar") - val outputServerShadowJar: Path - get() = path("server/build/libs/server-1.0-all.jar") + val outputServerShadowJar: JarPath + get() = jarPath("server/build/libs/server-1.0-all.jar") + + fun jarPath(path: String): JarPath { + val realPath = root.resolve(path).also { + check(it.exists()) { "Path not found: $it" } + check(it.isRegularFile()) { "Path is not a regular file: $it" } + } + return JarPath(realPath) + } fun path(path: String): Path { return root.resolve(path).also { - val extension = it.extension - // Binary files should not be created, text files should be created. - if (it.exists() || extension == "jar" || extension == "zip") return@also - + if (it.exists()) return@also it.parent.createDirectories() + // We should create text file only if it doesn't exist. it.createFile() } } @@ -143,22 +150,6 @@ abstract class BasePluginTest { return AppendableMavenFileRepository(root.resolve(path)) } - fun assertContains(jarPath: Path, paths: List) { - JarFile(jarPath.toFile()).use { jar -> - paths.forEach { path -> - assert(jar.getJarEntry(path) != null) { "Jar file $jarPath does not contain entry $path" } - } - } - } - - fun assertDoesNotContain(jarPath: Path, paths: List) { - JarFile(jarPath.toFile()).use { jar -> - paths.forEach { path -> - assert(jar.getJarEntry(path) == null) { "Jar file $jarPath contains entry $path" } - } - } - } - private val runner: GradleRunner get() { return GradleRunner.create() @@ -328,6 +319,12 @@ abstract class BasePluginTest { tasks.named('$SHADOW_RUN_TASK_NAME', ${JavaJarExec::class.java.name}) """.trimIndent() + fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } + + fun fromJar(vararg paths: Path): String { + return paths.joinToString(System.lineSeparator()) { "from('${it.toUri().toURL().path}')" } + } + fun BuildResult.assertNoDeprecationWarnings() = apply { output.lines().forEach { assert(!containsDeprecationWarning(it)) diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt index 05857c983..79ed273a2 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt @@ -2,11 +2,11 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.exists import assertk.assertions.isEqualTo import assertk.assertions.isNotNull +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import kotlin.io.path.appendText -import kotlin.io.path.deleteExisting import kotlin.io.path.writeText import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.TaskOutcome @@ -76,13 +76,12 @@ class ConfigurationCacheSpec : BasePluginTest() { outputShadowJar.deleteExisting() val result = run(shadowJarTask) - assertContains( - outputShadowJar, - listOf("a.properties", "b.properties"), + assertThat(outputShadowJar).containsEntries( + "a.properties", + "b.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf("a2.properties"), + assertThat(outputShadowJar).doesNotContainEntries( + "a2.properties", ) result.assertCcReused() } @@ -101,14 +100,12 @@ class ConfigurationCacheSpec : BasePluginTest() { outputServerShadowJar.deleteExisting() val result = run(shadowJarTask) - assertThat(outputServerShadowJar).exists() - assertContains( - outputServerShadowJar, - listOf("server/Server.class", "junit/framework/Test.class"), + assertThat(outputServerShadowJar).containsEntries( + "server/Server.class", + "junit/framework/Test.class", ) - assertDoesNotContain( - outputServerShadowJar, - listOf("client/Client.class"), + assertThat(outputServerShadowJar).doesNotContainEntries( + "client/Client.class", ) result.assertCcReused() } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 08c2f4cf5..add31e8e6 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -1,9 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat -import assertk.assertions.exists import assertk.assertions.isEqualTo import assertk.assertions.isNotNull +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText @@ -32,9 +33,10 @@ class FilteringTest : BasePluginTest() { @Test fun includeAllDependencies() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf("a.properties", "a2.properties", "b.properties"), + assertThat(outputShadowJar).containsEntries( + "a.properties", + "a2.properties", + "b.properties", ) } @@ -50,13 +52,12 @@ class FilteringTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf("a.properties", "b.properties"), + assertThat(outputShadowJar).containsEntries( + "a.properties", + "b.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf("a2.properties"), + assertThat(outputShadowJar).doesNotContainEntries( + "a2.properties", ) } @@ -107,13 +108,14 @@ class FilteringTest : BasePluginTest() { assertThat(result.task(":shadowJar")).isNotNull() .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) - assertContains( - outputShadowJar, - listOf("a.properties", "a2.properties", "b.properties", "d.properties"), + assertThat(outputShadowJar).containsEntries( + "a.properties", + "a2.properties", + "b.properties", + "d.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf("c.properties"), + assertThat(outputShadowJar).doesNotContainEntries( + "c.properties", ) } @@ -134,13 +136,14 @@ class FilteringTest : BasePluginTest() { assertThat(result.task(":shadowJar")).isNotNull() .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) - assertContains( - outputShadowJar, - listOf("a2.properties", "b.properties", "c.properties", "d.properties"), + assertThat(outputShadowJar).containsEntries( + "a2.properties", + "b.properties", + "c.properties", + "d.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf("a.properties"), + assertThat(outputShadowJar).doesNotContainEntries( + "a.properties", ) } @@ -168,13 +171,15 @@ class FilteringTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf("d.properties", "shadow/Passed.class"), + assertThat(outputShadowJar).containsEntries( + "d.properties", + "shadow/Passed.class", ) - assertDoesNotContain( - outputShadowJar, - listOf("a.properties", "a2.properties", "b.properties", "c.properties"), + assertThat(outputShadowJar).doesNotContainEntries( + "a.properties", + "a2.properties", + "b.properties", + "c.properties", ) } @@ -190,14 +195,12 @@ class FilteringTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).exists() - assertDoesNotContain( - outputServerShadowJar, - listOf("client/Client.class"), + assertThat(outputServerShadowJar).doesNotContainEntries( + "client/Client.class", ) - assertContains( - outputServerShadowJar, - listOf("server/Server.class", "junit/framework/Test.class"), + assertThat(outputServerShadowJar).containsEntries( + "server/Server.class", + "junit/framework/Test.class", ) } @@ -213,14 +216,12 @@ class FilteringTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).exists() - assertDoesNotContain( - outputServerShadowJar, - listOf("junit/framework/Test.class"), + assertThat(outputServerShadowJar).doesNotContainEntries( + "junit/framework/Test.class", ) - assertContains( - outputServerShadowJar, - listOf("client/Client.class", "server/Server.class"), + assertThat(outputServerShadowJar).containsEntries( + "client/Client.class", + "server/Server.class", ) } @@ -238,13 +239,12 @@ class FilteringTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf("a.properties", "b.properties"), + assertThat(outputShadowJar).containsEntries( + "a.properties", + "b.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf("a2.properties"), + assertThat(outputShadowJar).doesNotContainEntries( + "a2.properties", ) } @@ -274,13 +274,14 @@ class FilteringTest : BasePluginTest() { } private fun commonAssertions() { - assertContains( - outputShadowJar, - listOf("a.properties", "a2.properties", "b.properties", "c.properties"), + assertThat(outputShadowJar).containsEntries( + "a.properties", + "a2.properties", + "b.properties", + "c.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf("d.properties"), + assertThat(outputShadowJar).doesNotContainEntries( + "d.properties", ) } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 100d4e323..3a5038030 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -2,12 +2,12 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertFailure import assertk.assertThat -import assertk.assertions.exists import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import java.net.URLClassLoader -import java.util.jar.JarFile import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test @@ -28,26 +28,23 @@ class RelocationTest : BasePluginTest() { ) run(shadowJarTask) - assertContains( - outputShadowJar, - listOf( - "META-INF/MANIFEST.MF", - "shadow/junit/textui/ResultPrinter.class", - "shadow/junit/textui/TestRunner.class", - "shadow/junit/framework/Assert.class", - "shadow/junit/framework/AssertionFailedError.class", - "shadow/junit/framework/ComparisonCompactor.class", - "shadow/junit/framework/ComparisonFailure.class", - "shadow/junit/framework/Protectable.class", - "shadow/junit/framework/Test.class", - "shadow/junit/framework/TestCase.class", - "shadow/junit/framework/TestFailure.class", - "shadow/junit/framework/TestListener.class", - "shadow/junit/framework/TestResult$1.class", - "shadow/junit/framework/TestResult.class", - "shadow/junit/framework/TestSuite$1.class", - "shadow/junit/framework/TestSuite.class", - ), + assertThat(outputShadowJar).containsEntries( + "META-INF/MANIFEST.MF", + "shadow/junit/textui/ResultPrinter.class", + "shadow/junit/textui/TestRunner.class", + "shadow/junit/framework/Assert.class", + "shadow/junit/framework/AssertionFailedError.class", + "shadow/junit/framework/ComparisonCompactor.class", + "shadow/junit/framework/ComparisonFailure.class", + "shadow/junit/framework/Protectable.class", + "shadow/junit/framework/Test.class", + "shadow/junit/framework/TestCase.class", + "shadow/junit/framework/TestFailure.class", + "shadow/junit/framework/TestListener.class", + "shadow/junit/framework/TestResult$1.class", + "shadow/junit/framework/TestResult.class", + "shadow/junit/framework/TestSuite$1.class", + "shadow/junit/framework/TestSuite.class", ) } @@ -73,51 +70,44 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf( - "META-INF/MANIFEST.MF", - "a/ResultPrinter.class", - "a/TestRunner.class", - "b/Assert.class", - "b/AssertionFailedError.class", - "b/ComparisonCompactor.class", - "b/ComparisonFailure.class", - "b/Protectable.class", - "b/Test.class", - "b/TestCase.class", - "b/TestFailure.class", - "b/TestListener.class", - "b/TestResult\$1.class", - "b/TestResult.class", - "b/TestSuite\$1.class", - "b/TestSuite.class", - ), + assertThat(outputShadowJar).containsEntries( + "META-INF/MANIFEST.MF", + "a/ResultPrinter.class", + "a/TestRunner.class", + "b/Assert.class", + "b/AssertionFailedError.class", + "b/ComparisonCompactor.class", + "b/ComparisonFailure.class", + "b/Protectable.class", + "b/Test.class", + "b/TestCase.class", + "b/TestFailure.class", + "b/TestListener.class", + "b/TestResult\$1.class", + "b/TestResult.class", + "b/TestSuite\$1.class", + "b/TestSuite.class", ) - assertDoesNotContain( - outputShadowJar, - listOf( - "junit/textui/ResultPrinter.class", - "junit/textui/TestRunner.class", - "junit/framework/Assert.class", - "junit/framework/AssertionFailedError.class", - "junit/framework/ComparisonCompactor.class", - "junit/framework/ComparisonFailure.class", - "junit/framework/Protectable.class", - "junit/framework/Test.class", - "junit/framework/TestCase.class", - "junit/framework/TestFailure.class", - "junit/framework/TestListener.class", - "junit/framework/TestResult\$1.class", - "junit/framework/TestResult.class", - "junit/framework/TestSuite\$1.class", - "junit/framework/TestSuite.class", - ), + assertThat(outputShadowJar).doesNotContainEntries( + "junit/textui/ResultPrinter.class", + "junit/textui/TestRunner.class", + "junit/framework/Assert.class", + "junit/framework/AssertionFailedError.class", + "junit/framework/ComparisonCompactor.class", + "junit/framework/ComparisonFailure.class", + "junit/framework/Protectable.class", + "junit/framework/Test.class", + "junit/framework/TestCase.class", + "junit/framework/TestFailure.class", + "junit/framework/TestListener.class", + "junit/framework/TestResult\$1.class", + "junit/framework/TestResult.class", + "junit/framework/TestSuite\$1.class", + "junit/framework/TestSuite.class", ) - - val jarFile = JarFile(outputShadowJar.toFile()) - assertThat(jarFile.manifest.mainAttributes.getValue("TEST-VALUE")).isEqualTo("FOO") + assertThat(outputShadowJar.manifest.mainAttributes.getValue("TEST-VALUE")) + .isEqualTo("FOO") } @Test @@ -140,37 +130,31 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf( - "a/ResultPrinter.class", - "b/Test.class", - "b/TestCase.class", - "b/TestFailure.class", - "b/TestListener.class", - "b/TestResult\$1.class", - "b/TestResult.class", - "b/TestSuite\$1.class", - "b/TestSuite.class", - "junit/textui/TestRunner.class", - "junit/framework/Assert.class", - "junit/framework/AssertionFailedError.class", - "junit/framework/ComparisonCompactor.class", - "junit/framework/ComparisonFailure.class", - "junit/framework/Protectable.class", - ), + assertThat(outputShadowJar).containsEntries( + "a/ResultPrinter.class", + "b/Test.class", + "b/TestCase.class", + "b/TestFailure.class", + "b/TestListener.class", + "b/TestResult\$1.class", + "b/TestResult.class", + "b/TestSuite\$1.class", + "b/TestSuite.class", + "junit/textui/TestRunner.class", + "junit/framework/Assert.class", + "junit/framework/AssertionFailedError.class", + "junit/framework/ComparisonCompactor.class", + "junit/framework/ComparisonFailure.class", + "junit/framework/Protectable.class", ) - assertDoesNotContain( - outputShadowJar, - listOf( - "a/TestRunner.class", - "b/Assert.class", - "b/AssertionFailedError.class", - "b/ComparisonCompactor.class", - "b/ComparisonFailure.class", - "b/Protectable.class", - ), + assertThat(outputShadowJar).doesNotContainEntries( + "a/TestRunner.class", + "b/Assert.class", + "b/AssertionFailedError.class", + "b/ComparisonCompactor.class", + "b/ComparisonFailure.class", + "b/Protectable.class", ) } @@ -207,21 +191,15 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf( - "shadow/ShadowTest.class", - "shadow/junit/Test.class", - "shadow/junit", - ), + assertThat(outputShadowJar).containsEntries( + "shadow/ShadowTest.class", + "shadow/junit/Test.class", + "shadow/junit", ) - assertDoesNotContain( - outputShadowJar, - listOf( - "junit/framework", - "junit/framework/Test.class", - ), + assertThat(outputShadowJar).doesNotContainEntries( + "junit/framework", + "junit/framework/Test.class", ) val classLoader = URLClassLoader( @@ -293,19 +271,14 @@ class RelocationTest : BasePluginTest() { run(":app:$shadowJarTask") - val appOutput = path("app/build/libs/app-all.jar") - assertThat(appOutput).exists() - - assertContains( - appOutput, - listOf( - "TEST", - "APP-TEST", - "test.properties", - "app/core/Core.class", - "app/App.class", - "app/junit/framework/Test.class", - ), + val appOutput = jarPath("app/build/libs/app-all.jar") + assertThat(appOutput).containsEntries( + "TEST", + "APP-TEST", + "test.properties", + "app/core/Core.class", + "app/App.class", + "app/junit/framework/Test.class", ) } @@ -340,21 +313,15 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf( - "bar/Foo.class", - "bar/foo.properties", - "bar/dep.properties", - ), + assertThat(outputShadowJar).containsEntries( + "bar/Foo.class", + "bar/foo.properties", + "bar/dep.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf( - "foo/Foo.class", - "foo/foo.properties", - "foo/dep.properties", - ), + assertThat(outputShadowJar).doesNotContainEntries( + "foo/Foo.class", + "foo/foo.properties", + "foo/dep.properties", ) } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt index 7aa7674ff..67f71634e 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.exists import assertk.assertions.isEqualTo import assertk.assertions.isNotNull import assertk.assertions.isNull @@ -10,9 +9,12 @@ import assertk.assertions.isTrue import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.Issue -import java.util.jar.JarFile +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.isRegular import kotlin.io.path.appendText import kotlin.io.path.readText +import kotlin.io.path.toPath import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin import org.gradle.testfixtures.ProjectBuilder @@ -69,7 +71,7 @@ class ShadowPluginTest : BasePluginTest() { it.withGradleVersion("8.3") } - assertThat(outputShadowJar).exists() + assertThat(outputShadowJar).isRegular() } @Test @@ -81,23 +83,22 @@ class ShadowPluginTest : BasePluginTest() { @Test fun shadowCopy() { - val artifactJarPath = requireNotNull(this::class.java.classLoader.getResource("test-artifact-1.0-SNAPSHOT.jar")) - .path - val projectJarPath = requireNotNull(this::class.java.classLoader.getResource("test-project-1.0-SNAPSHOT.jar")) - .path + val artifactJar = requireNotNull(this::class.java.classLoader.getResource("test-artifact-1.0-SNAPSHOT.jar")) + .toURI().toPath() + val projectJar = requireNotNull(this::class.java.classLoader.getResource("test-project-1.0-SNAPSHOT.jar")) + .toURI().toPath() projectScriptPath.appendText( """ $shadowJar { - from('$artifactJarPath') - from('$projectJarPath') + ${fromJar(artifactJar, projectJar)} } """.trimIndent(), ) run(shadowJarTask) - assertThat(outputShadowJar).exists() + assertThat(outputShadowJar).isRegular() } @Test @@ -123,16 +124,15 @@ class ShadowPluginTest : BasePluginTest() { ) run(shadowJarTask) - val outputShadowJar = path("build/libs/shadow.jar") - assertThat(outputShadowJar).exists() - assertContains( - outputShadowJar, - listOf("shadow/Passed.class", "junit/framework/Test.class"), + val outputShadowJar = jarPath("build/libs/shadow.jar") + + assertThat(outputShadowJar).containsEntries( + "shadow/Passed.class", + "junit/framework/Test.class", ) - assertDoesNotContain( - outputShadowJar, - listOf("/"), + assertThat(outputShadowJar).doesNotContainEntries( + "/", ) } @@ -142,10 +142,10 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).exists() - assertContains( - outputServerShadowJar, - listOf("client/Client.class", "server/Server.class", "junit/framework/Test.class"), + assertThat(outputServerShadowJar).containsEntries( + "client/Client.class", + "server/Server.class", + "junit/framework/Test.class", ) } @@ -173,14 +173,12 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).exists() - assertContains( - outputServerShadowJar, - listOf("client/Client.class", "server/Server.class"), + assertThat(outputServerShadowJar).containsEntries( + "client/Client.class", + "server/Server.class", ) - assertDoesNotContain( - outputServerShadowJar, - listOf("junit/framework/Test.class"), + assertThat(outputServerShadowJar).doesNotContainEntries( + "junit/framework/Test.class", ) } @@ -201,14 +199,12 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).exists() - assertContains( - outputServerShadowJar, - listOf("server/Server.class", "junit/framework/Test.class"), + assertThat(outputServerShadowJar).containsEntries( + "server/Server.class", + "junit/framework/Test.class", ) - assertDoesNotContain( - outputServerShadowJar, - listOf("client/Client.class"), + assertThat(outputServerShadowJar).doesNotContainEntries( + "client/Client.class", ) } @@ -228,10 +224,9 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).exists() - assertContains( - outputServerShadowJar, - listOf("client/Client.class", "server/Server.class"), + assertThat(outputServerShadowJar).containsEntries( + "client/Client.class", + "server/Server.class", ) } @@ -260,10 +255,10 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).exists() - assertContains( - outputServerShadowJar, - listOf("client/Client.class", "server/Server.class", "junit/framework/TestCase.class"), + assertThat(outputServerShadowJar).containsEntries( + "client/Client.class", + "server/Server.class", + "junit/framework/TestCase.class", ) path("client/src/main/java/client/Client.java").writeText( @@ -273,12 +268,13 @@ class ShadowPluginTest : BasePluginTest() { """.trimIndent(), ) run(serverShadowJarTask) - assertThat(outputServerShadowJar).exists() + // TODO: I don't think junit classes should be in the output jar, but it's the test case // from https://github.com/GradleUp/shadow/pull/420, need to investigate more... - assertContains( - outputServerShadowJar, - listOf("client/Client.class", "server/Server.class", "junit/framework/TestCase.class"), + assertThat(outputServerShadowJar).containsEntries( + "client/Client.class", + "server/Server.class", + "junit/framework/TestCase.class", ) } @@ -292,16 +288,17 @@ class ShadowPluginTest : BasePluginTest() { writeApiLibAndImplModules() run(":impl:$shadowJarTask") - val implOutput = path("impl/build/libs/impl-all.jar") - assertThat(implOutput).exists() - assertContains( - implOutput, - listOf("impl/SimpleEntity.class", "api/Entity.class", "api/UnusedEntity.class", "lib/LibEntity.class"), + val implOutput = jarPath("impl/build/libs/impl-all.jar") + assertThat(implOutput).containsEntries( + "impl/SimpleEntity.class", + "api/Entity.class", + "api/UnusedEntity.class", + "lib/LibEntity.class", ) - assertDoesNotContain( - implOutput, - listOf("junit/framework/Test.class", "lib/UnusedLibEntity.class"), + assertThat(implOutput).doesNotContainEntries( + "junit/framework/Test.class", + "lib/UnusedLibEntity.class", ) } @@ -324,22 +321,17 @@ class ShadowPluginTest : BasePluginTest() { ) run(":impl:$shadowJarTask") - val implOutput = path("impl/build/libs/impl-all.jar") - - assertThat(implOutput).exists() - assertContains( - implOutput, - listOf( - "impl/SimpleEntity.class", - "api/Entity.class", - "api/UnusedEntity.class", - "lib/LibEntity.class", - "lib/UnusedLibEntity.class", - ), - ) - assertDoesNotContain( - implOutput, - listOf("junit/framework/Test.class"), + + val implOutput = jarPath("impl/build/libs/impl-all.jar") + assertThat(implOutput).containsEntries( + "impl/SimpleEntity.class", + "api/Entity.class", + "api/UnusedEntity.class", + "lib/LibEntity.class", + "lib/UnusedLibEntity.class", + ) + assertThat(implOutput).doesNotContainEntries( + "junit/framework/Test.class", ) } @@ -348,22 +340,21 @@ class ShadowPluginTest : BasePluginTest() { writeShadowedClientAndServerModules() run(":server:jar") - val serverOutput = path("server/build/libs/server-1.0.jar") - val clientOutput = path("client/build/libs/client-all.jar") - assertThat(serverOutput).exists() - assertContains( - serverOutput, - listOf("server/Server.class"), + val serverOutput = jarPath("server/build/libs/server-1.0.jar") + assertThat(serverOutput).containsEntries( + "server/Server.class", ) - assertDoesNotContain( - serverOutput, - listOf("client/Client.class", "junit/framework/Test.class", "client/junit/framework/Test.class"), + assertThat(serverOutput).doesNotContainEntries( + "client/Client.class", + "junit/framework/Test.class", + "client/junit/framework/Test.class", ) - assertThat(clientOutput).exists() - assertContains( - clientOutput, - listOf("client/Client.class", "client/junit/framework/Test.class"), + + val clientOutput = jarPath("client/build/libs/client-all.jar") + assertThat(clientOutput).containsEntries( + "client/Client.class", + "client/junit/framework/Test.class", ) } @@ -372,21 +363,20 @@ class ShadowPluginTest : BasePluginTest() { writeShadowedClientAndServerModules() run(serverShadowJarTask) - val clientOutput = path("client/build/libs/client-all.jar") - assertThat(outputServerShadowJar).exists() - assertContains( - outputServerShadowJar, - listOf("client/Client.class", "client/junit/framework/Test.class", "server/Server.class"), + assertThat(outputServerShadowJar).containsEntries( + "client/Client.class", + "client/junit/framework/Test.class", + "server/Server.class", ) - assertDoesNotContain( - outputServerShadowJar, - listOf("junit/framework/Test.class"), + assertThat(outputServerShadowJar).doesNotContainEntries( + "junit/framework/Test.class", ) - assertThat(clientOutput).exists() - assertContains( - clientOutput, - listOf("client/Client.class", "client/junit/framework/Test.class"), + + val clientOutput = jarPath("client/build/libs/client-all.jar") + assertThat(clientOutput).containsEntries( + "client/Client.class", + "client/junit/framework/Test.class", ) } @@ -418,13 +408,17 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf("shadow/Passed.class", "a.properties", "META-INF/a.properties"), + assertThat(outputShadowJar).containsEntries( + "shadow/Passed.class", + "a.properties", + "META-INF/a.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf("META-INF/INDEX.LIST", "META-INF/a.SF", "META-INF/a.DSA", "META-INF/a.RSA", "module-info.class"), + assertThat(outputShadowJar).doesNotContainEntries( + "META-INF/INDEX.LIST", + "META-INF/a.SF", + "META-INF/a.DSA", + "META-INF/a.RSA", + "module-info.class", ) } @@ -444,13 +438,12 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf("a.properties", "a2.properties"), + assertThat(outputShadowJar).containsEntries( + "a.properties", + "a2.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf("b.properties"), + assertThat(outputShadowJar).doesNotContainEntries( + "b.properties", ) } @@ -483,9 +476,11 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf("api.properties", "implementation.properties", "runtimeOnly.properties", "implementation-dep.properties"), + assertThat(outputShadowJar).containsEntries( + "api.properties", + "implementation.properties", + "runtimeOnly.properties", + "implementation-dep.properties", ) } @@ -505,13 +500,12 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertContains( - outputShadowJar, - listOf("a.properties", "a2.properties"), + assertThat(outputShadowJar).containsEntries( + "a.properties", + "a2.properties", ) - assertDoesNotContain( - outputShadowJar, - listOf("b.properties"), + assertThat(outputShadowJar).doesNotContainEntries( + "b.properties", ) } @@ -535,9 +529,7 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).exists() - val entries = JarFile(outputShadowJar.toFile()).entries().toList() - assertThat(entries.size).isEqualTo(2) + assertThat(outputShadowJar.entries().toList().size).isEqualTo(2) } @Test @@ -552,9 +544,7 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).exists() - val attributes = JarFile(outputShadowJar.toFile()).manifest.mainAttributes - assertThat(attributes.getValue("Class-Path")).isNull() + assertThat(outputShadowJar.manifest.mainAttributes.getValue("Class-Path")).isNull() } @Issue( @@ -577,9 +567,8 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).exists() - val attributes = JarFile(outputShadowJar.toFile()).manifest.mainAttributes - assertThat(attributes.getValue("Class-Path")).isEqualTo("/libs/a.jar junit-3.8.2.jar") + assertThat(outputShadowJar.manifest.mainAttributes.getValue("Class-Path")) + .isEqualTo("/libs/a.jar junit-3.8.2.jar") } @Issue( @@ -597,9 +586,8 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).exists() - val attributes = JarFile(outputShadowJar.toFile()).manifest.mainAttributes - assertThat(attributes.getValue("Class-Path")).isEqualTo("junit-3.8.2.jar") + assertThat(outputShadowJar.manifest.mainAttributes.getValue("Class-Path")) + .isEqualTo("junit-3.8.2.jar") } @Issue( @@ -621,7 +609,7 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).exists() + assertThat(outputShadowJar).isRegular() } /** @@ -755,9 +743,7 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).exists() - - val entries = JarFile(outputShadowJar.toFile()).entries().toList() + val entries = outputShadowJar.entries().toList() assertThat(entries.count { it.name.endsWith(".class") }).isEqualTo(1) } @@ -783,13 +769,11 @@ class ShadowPluginTest : BasePluginTest() { ) val result = run(testShadowJarTask) - val testJar = path("build/libs/shadow-1.0-tests.jar") assertThat(result.task(":$testShadowJarTask")).isNotNull() .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) - - assertThat(testJar).exists() - assertThat(JarFile(testJar.toFile()).getEntry("junit")).isNotNull() + val testJar = jarPath("build/libs/shadow-1.0-tests.jar") + assertThat(testJar.getEntry("junit")).isNotNull() } private fun writeShadowedClientAndServerModules() { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt new file mode 100644 index 000000000..24e12cd09 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -0,0 +1,54 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import kotlin.io.path.appendText +import org.junit.jupiter.api.Test + +class AppendingTransformerTest : BaseTransformerTest() { + @Test + fun appendingTransformer() { + val one = buildJarOne { + insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) + } + val two = buildJarTwo { + insert(ENTRY_TEST_PROPERTIES, CONTENT_TWO) + } + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar(one, two), + transformerBlock = """ + resource = '$ENTRY_TEST_PROPERTIES' + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar.getContent(ENTRY_TEST_PROPERTIES).trimIndent()) + .isEqualTo(CONTENT_ONE_TWO) + } + + @Test + fun appendingTransformerShortSyntax() { + val one = buildJarOne { + insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) + } + val two = buildJarTwo { + insert(ENTRY_TEST_PROPERTIES, CONTENT_TWO) + } + projectScriptPath.appendText( + """ + $shadowJar { + ${fromJar(one, two)} + append('$ENTRY_TEST_PROPERTIES') + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar.getContent(ENTRY_TEST_PROPERTIES).trimIndent()) + .isEqualTo(CONTENT_ONE_TWO) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt new file mode 100644 index 000000000..c38ad8a12 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -0,0 +1,70 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.BasePluginTest +import com.github.jengelman.gradle.plugins.shadow.util.AppendableJar +import java.nio.file.Path +import kotlin.io.path.writeText + +sealed class BaseTransformerTest : BasePluginTest() { + + fun buildJarOne( + builder: AppendableJar.() -> Unit = { + insert(ENTRY_SERVICES_SHADE, CONTENT_ONE) + insert(ENTRY_SERVICES_FOO, "one") + }, + ): Path { + return buildJar("one.jar", builder) + } + + fun buildJarTwo( + builder: AppendableJar.() -> Unit = { + insert(ENTRY_SERVICES_SHADE, CONTENT_TWO) + insert(ENTRY_SERVICES_FOO, "two") + }, + ): Path { + return buildJar("two.jar", builder) + } + + inline fun buildJar(path: String, builder: AppendableJar.() -> Unit): Path { + return AppendableJar(path(path)).apply(builder).write() + } + + fun writeMainClass() { + path("src/main/java/shadow/Main.java").writeText( + """ + package shadow; + public class Main { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } + """.trimIndent(), + ) + } + + protected companion object { + const val CONTENT_ONE = "one # NOTE: No newline terminates this line/file" + const val CONTENT_TWO = "two # NOTE: No newline terminates this line/file" + const val CONTENT_THREE = "three # NOTE: No newline terminates this line/file" + const val CONTENT_ONE_TWO = "$CONTENT_ONE\n$CONTENT_TWO" + + const val ENTRY_TEST_PROPERTIES = "test.properties" + const val ENTRY_SERVICES_SHADE = "META-INF/services/org.apache.maven.Shade" + const val ENTRY_SERVICES_FOO = "META-INF/services/com.acme.Foo" + const val ENTRY_FOO_SHADE = "META-INF/foo/org.apache.maven.Shade" + + inline fun transform( + shadowJarBlock: String = "", + transformerBlock: String = "", + ): String { + return """ + $shadowJar { + $shadowJarBlock + transform(${T::class.java.name}) { + $transformerBlock + } + } + """.trimIndent() + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt new file mode 100644 index 000000000..f12eae119 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -0,0 +1,110 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.EXTENSION_CLASSES_KEY +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_NAME +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_VERSION +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MODULE_NAME_KEY +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MODULE_VERSION_KEY +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.STATIC_EXTENSION_CLASSES_KEY +import java.nio.file.Path +import java.util.Properties +import kotlin.io.path.appendText +import org.junit.jupiter.api.Test + +class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { + @Test + fun groovyExtensionModuleTransformer() { + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar(buildJarFoo(), buildJarBar()), + ), + ) + + run(shadowJarTask) + + val props = outputShadowJar.getContent(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH).toProperties() + commonAssertions(props) + } + + @Test + fun groovyExtensionModuleTransformerWorksForLegacyGroovy() { + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar( + buildJarFoo(GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH), + buildJarBar(GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH), + ), + ), + ) + + run(shadowJarTask) + + val props = outputShadowJar.getContent(GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH).toProperties() + commonAssertions(props) + } + + @Test + fun groovyExtensionModuleTransformerShortSyntax() { + projectScriptPath.appendText( + """ + $shadowJar { + ${fromJar(buildJarFoo(), buildJarBar())} + mergeGroovyExtensionModules() + } + """.trimIndent(), + ) + + run(shadowJarTask) + + val props = outputShadowJar.getContent(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH).toProperties() + commonAssertions(props) + } + + private fun buildJarFoo( + path: String = GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH, + ): Path = buildJar("foo.jar") { + insert( + path, + """ + $MODULE_NAME_KEY=foo + $MODULE_VERSION_KEY=1.0.5 + $EXTENSION_CLASSES_KEY=$EXTENSION_CLASSES_FOO + $STATIC_EXTENSION_CLASSES_KEY=$STATIC_EXTENSION_CLASSES_FOO + """.trimIndent(), + ) + } + + private fun buildJarBar( + path: String = GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH, + ): Path = buildJar("bar.jar") { + insert( + path, + """ + $MODULE_NAME_KEY=bar + $MODULE_VERSION_KEY=2.3.5 + $EXTENSION_CLASSES_KEY=$EXTENSION_CLASSES_BAR + $STATIC_EXTENSION_CLASSES_KEY=$STATIC_EXTENSION_CLASSES_BAR + """.trimIndent(), + ) + } + + private companion object { + const val EXTENSION_CLASSES_FOO = "com.acme.foo.FooExtension,com.acme.foo.BarExtension" + const val EXTENSION_CLASSES_BAR = "com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension" + const val STATIC_EXTENSION_CLASSES_FOO = "com.acme.foo.FooStaticExtension" + const val STATIC_EXTENSION_CLASSES_BAR = "com.acme.bar.SomeStaticExtension" + + fun commonAssertions(properties: Properties) { + assertThat(properties.getProperty(MODULE_NAME_KEY)).isEqualTo(MERGED_MODULE_NAME) + assertThat(properties.getProperty(MODULE_VERSION_KEY)).isEqualTo(MERGED_MODULE_VERSION) + assertThat(properties.getProperty(EXTENSION_CLASSES_KEY)) + .isEqualTo("$EXTENSION_CLASSES_FOO,$EXTENSION_CLASSES_BAR") + assertThat(properties.getProperty(STATIC_EXTENSION_CLASSES_KEY)) + .isEqualTo("$STATIC_EXTENSION_CLASSES_FOO,$STATIC_EXTENSION_CLASSES_BAR") + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt new file mode 100644 index 000000000..00de08e5a --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -0,0 +1,203 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.util.Issue +import kotlin.io.path.appendText +import kotlin.io.path.writeText +import org.junit.jupiter.api.Test + +class ServiceFileTransformerTest : BaseTransformerTest() { + + @Test + fun serviceResourceTransformer() { + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar(buildJarOne(), buildJarTwo()), + transformerBlock = """ + exclude 'META-INF/services/com.acme.*' + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar.getContent(ENTRY_SERVICES_SHADE)).isEqualTo(CONTENT_ONE_TWO) + assertThat(outputShadowJar.getContent(ENTRY_SERVICES_FOO)).isEqualTo("one") + } + + @Test + fun serviceResourceTransformerAlternatePath() { + val one = buildJarOne { + insert(ENTRY_FOO_SHADE, CONTENT_ONE) + } + val two = buildJarTwo { + insert(ENTRY_FOO_SHADE, CONTENT_TWO) + } + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar(one, two), + transformerBlock = """ + path = 'META-INF/foo' + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar.getContent(ENTRY_FOO_SHADE)).isEqualTo(CONTENT_ONE_TWO) + } + + @Test + fun serviceResourceTransformerShortSyntax() { + projectScriptPath.appendText( + """ + $shadowJar { + ${fromJar(buildJarOne(), buildJarTwo())} + mergeServiceFiles { + exclude 'META-INF/services/com.acme.*' + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar.getContent(ENTRY_SERVICES_SHADE)).isEqualTo(CONTENT_ONE_TWO) + assertThat(outputShadowJar.getContent(ENTRY_SERVICES_FOO)).isEqualTo("one") + } + + @Test + fun serviceResourceTransformerShortSyntaxRelocation() { + val one = buildJarOne { + insert( + "META-INF/services/java.sql.Driver", + """ + oracle.jdbc.OracleDriver + org.apache.hive.jdbc.HiveDriver + """.trimIndent(), + ) + insert( + "META-INF/services/org.apache.axis.components.compiler.Compiler", + "org.apache.axis.components.compiler.Javac", + ) + insert( + "META-INF/services/org.apache.commons.logging.LogFactory", + "org.apache.commons.logging.impl.LogFactoryImpl", + ) + } + val two = buildJarTwo { + insert( + "META-INF/services/java.sql.Driver", + """ + org.apache.derby.jdbc.AutoloadedDriver + com.mysql.jdbc.Driver + """.trimIndent(), + ) + insert( + "META-INF/services/org.apache.axis.components.compiler.Compiler", + "org.apache.axis.components.compiler.Jikes", + ) + insert( + "META-INF/services/org.apache.commons.logging.LogFactory", + "org.mortbay.log.Factory", + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + ${fromJar(one, two)} + mergeServiceFiles() + relocate("org.apache", "myapache") { + exclude 'org.apache.axis.components.compiler.Jikes' + exclude 'org.apache.commons.logging.LogFactory' + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + val text1 = outputShadowJar.getContent("META-INF/services/java.sql.Driver") + assertThat(text1).isEqualTo( + """ + oracle.jdbc.OracleDriver + myapache.hive.jdbc.HiveDriver + myapache.derby.jdbc.AutoloadedDriver + com.mysql.jdbc.Driver + """.trimIndent(), + ) + + val text2 = outputShadowJar.getContent("META-INF/services/myapache.axis.components.compiler.Compiler") + assertThat(text2).isEqualTo( + """ + myapache.axis.components.compiler.Javac + org.apache.axis.components.compiler.Jikes + """.trimIndent(), + ) + + val text3 = outputShadowJar.getContent("META-INF/services/org.apache.commons.logging.LogFactory") + assertThat(text3).isEqualTo( + """ + myapache.commons.logging.impl.LogFactoryImpl + org.mortbay.log.Factory + """.trimIndent(), + ) + } + + @Test + fun serviceResourceTransformerShortSyntaxAlternatePath() { + val one = buildJarOne { + insert(ENTRY_FOO_SHADE, CONTENT_ONE) + } + val two = buildJarTwo { + insert(ENTRY_FOO_SHADE, CONTENT_TWO) + } + projectScriptPath.appendText( + """ + $shadowJar { + ${fromJar(one, two)} + mergeServiceFiles("META-INF/foo") + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar.getContent(ENTRY_FOO_SHADE)).isEqualTo(CONTENT_ONE_TWO) + } + + @Issue( + "https://github.com/GradleUp/shadow/issues/70", + "https://github.com/GradleUp/shadow/issues/71", + ) + @Test + fun applyTransformersToProjectResources() { + val servicesShadowEntry = "META-INF/services/shadow.Shadow" + val one = buildJarOne { + insert(servicesShadowEntry, CONTENT_ONE) + }.toUri().toURL().path + repo.module("shadow", "two", "1.0") + .insertFile(servicesShadowEntry, CONTENT_TWO) + .publish() + + projectScriptPath.appendText( + """ + dependencies { + implementation 'shadow:two:1.0' + implementation files('$one') + } + $shadowJar { + mergeServiceFiles() + } + """.trimIndent(), + ) + path("src/main/resources/$servicesShadowEntry").writeText(CONTENT_THREE) + + run(shadowJarTask) + + assertThat(outputShadowJar.getContent(servicesShadowEntry)) + .isEqualTo(CONTENT_THREE + "\n" + CONTENT_ONE_TWO) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt new file mode 100644 index 000000000..425828c5d --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -0,0 +1,166 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import assertk.assertions.isNull +import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.isRegular +import kotlin.io.path.appendText +import kotlin.io.path.writeText +import kotlin.reflect.KClass +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +class TransformersTest : BaseTransformerTest() { + + @Test + fun manifestRetained() { + writeMainClass() + projectScriptPath.appendText( + """ + jar { + manifest { + attributes 'Main-Class': 'shadow.Main' + attributes 'Test-Entry': 'PASSED' + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + val mf = outputShadowJar.manifest + assertThat(mf).isNotNull() + assertThat(mf.mainAttributes.getValue("Test-Entry")).isEqualTo("PASSED") + assertThat(mf.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") + } + + @Test + fun manifestTransformed() { + writeMainClass() + + projectScriptPath.appendText(MANIFEST_ATTRS) + + run(shadowJarTask) + + val mf = outputShadowJar.manifest + assertThat(mf).isNotNull() + assertThat(mf.mainAttributes.getValue("Test-Entry")).isEqualTo("PASSED") + assertThat(mf.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") + assertThat(mf.mainAttributes.getValue("New-Entry")).isEqualTo("NEW") + } + + @Test + fun appendXmlFiles() { + val propertiesXml = "properties.xml" + val xmlContent = """ + + + + %s + + """.trimIndent() + + val xml1 = buildJar("xml1.jar") { + insert(propertiesXml, xmlContent.format("key1", "val1")) + } + val xml2 = buildJar("xml2.jar") { + insert(propertiesXml, xmlContent.format("key2", "val2")) + } + + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar(xml1, xml2), + transformerBlock = """ + resource = 'properties.xml' + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar.getContent(propertiesXml).trimIndent()).isEqualTo( + """ + + + + val1 + val2 + + """.trimIndent(), + ) + } + + @Issue("https://github.com/GradleUp/shadow/issues/82") + @Test + fun shadowManifestLeaksToJarManifest() { + writeMainClass() + projectScriptPath.appendText(MANIFEST_ATTRS) + + run("jar", shadowJarTask) + + val mf1 = outputShadowJar.manifest + assertThat(mf1).isNotNull() + assertThat(mf1.mainAttributes.getValue("Test-Entry")).isEqualTo("PASSED") + assertThat(mf1.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") + assertThat(mf1.mainAttributes.getValue("New-Entry")).isEqualTo("NEW") + + val mf2 = jarPath("build/libs/shadow-1.0.jar").manifest + assertThat(mf2).isNotNull() + assertThat(mf2.mainAttributes.getValue("Test-Entry")).isEqualTo("FAILED") + assertThat(mf2.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") + assertThat(mf2.mainAttributes.getValue("New-Entry")).isNull() + } + + @ParameterizedTest + @MethodSource("transformerConfigurations") + fun otherTransformers(pair: Pair>) { + val (configuration, transformer) = pair + if (configuration.contains("test/some.file")) { + path("test/some.file").writeText("some content") + } + projectScriptPath.appendText( + """ + $shadowJar { + transform(${transformer.java.name}) $configuration + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).isRegular() + } + + private companion object { + val MANIFEST_ATTRS = """ + jar { + manifest { + attributes 'Main-Class': 'shadow.Main' + attributes 'Test-Entry': 'FAILED' + } + } + $shadowJar { + manifest { + attributes 'New-Entry': 'NEW' + attributes 'Test-Entry': 'PASSED' + } + } + """.trimIndent() + + @JvmStatic + fun transformerConfigurations() = listOf( + "" to ApacheLicenseResourceTransformer::class, + "" to ApacheNoticeResourceTransformer::class, + "" to ComponentsXmlResourceTransformer::class, + "" to DontIncludeResourceTransformer::class, + "{ resource.set(\"test.file\"); file.fileValue(file(\"test/some.file\")) }" to IncludeResourceTransformer::class, + "" to Log4j2PluginsCacheFileTransformer::class, + "" to ManifestAppenderTransformer::class, + "" to ManifestResourceTransformer::class, + "{ keyTransformer = { it.toLowerCase() } }" to PropertiesFileTransformer::class, + ) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt new file mode 100644 index 000000000..9f9ab7409 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -0,0 +1,47 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import assertk.Assert +import assertk.assertions.isNotEmpty +import assertk.fail +import java.nio.file.Path +import java.util.jar.JarFile +import kotlin.io.path.deleteExisting + +/** + * A wrapper for [JarFile] that also implements [Path]. + * + * We must declare some functions like [kotlin.io.path.deleteExisting] explicitly as they could not + * be delegated to [JarPath] type. + */ +class JarPath(val path: Path) : + JarFile(path.toFile()), + Path by path { + + fun deleteExisting() { + close() + path.deleteExisting() + } + + fun getContent(entryName: String): String { + val entry = getEntry(entryName) ?: error("Entry not found: $entryName") + return getInputStream(entry).bufferedReader().readText() + } +} + +/** + * Common regular assertions for [JarPath]. + */ +fun Assert.isRegular() = transform { it.entries().toList() }.isNotEmpty() + +fun Assert.containsEntries(vararg entries: String) = transform { actual -> + entries.forEach { entry -> + actual.getEntry(entry) ?: fail("Jar file ${actual.path} does not contain entry $entry") + } +} + +fun Assert.doesNotContainEntries(vararg entries: String) = transform { actual -> + entries.forEach { entry -> + actual.getEntry(entry) ?: return@forEach + fail("Jar file ${actual.path} contains entry $entry") + } +} From 59d49d387df113f595b4e00b752fa22e4e2abd4d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 5 Jan 2025 21:12:04 -0500 Subject: [PATCH 124/941] Rename const values (#1130) --- api/shadow.api | 12 ++--- .../GroovyExtensionModuleTransformerTest.kt | 50 +++++++++---------- .../GroovyExtensionModuleTransformer.kt | 28 ++++++----- 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index bbee96bb6..f25735c69 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -435,14 +435,14 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/DontInclude public class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer$Companion; - public static final field EXTENSION_CLASSES_KEY Ljava/lang/String; - public static final field GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH Ljava/lang/String; - public static final field GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH Ljava/lang/String; + public static final field KEY_EXTENSION_CLASSES Ljava/lang/String; + public static final field KEY_MODULE_NAME Ljava/lang/String; + public static final field KEY_MODULE_VERSION Ljava/lang/String; + public static final field KEY_STATIC_EXTENSION_CLASSES Ljava/lang/String; public static final field MERGED_MODULE_NAME Ljava/lang/String; public static final field MERGED_MODULE_VERSION Ljava/lang/String; - public static final field MODULE_NAME_KEY Ljava/lang/String; - public static final field MODULE_VERSION_KEY Ljava/lang/String; - public static final field STATIC_EXTENSION_CLASSES_KEY Ljava/lang/String; + public static final field PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR Ljava/lang/String; + public static final field PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR Ljava/lang/String; public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun hasTransformedResource ()Z diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index f12eae119..8838f4cd8 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -2,14 +2,14 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo -import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.EXTENSION_CLASSES_KEY -import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH -import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.KEY_EXTENSION_CLASSES +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.KEY_MODULE_NAME +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.KEY_MODULE_VERSION +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.KEY_STATIC_EXTENSION_CLASSES import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_NAME import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_VERSION -import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MODULE_NAME_KEY -import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MODULE_VERSION_KEY -import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.STATIC_EXTENSION_CLASSES_KEY +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR import java.nio.file.Path import java.util.Properties import kotlin.io.path.appendText @@ -26,7 +26,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { run(shadowJarTask) - val props = outputShadowJar.getContent(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH).toProperties() + val props = outputShadowJar.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR).toProperties() commonAssertions(props) } @@ -35,15 +35,15 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { projectScriptPath.appendText( transform( shadowJarBlock = fromJar( - buildJarFoo(GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH), - buildJarBar(GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH), + buildJarFoo(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + buildJarBar(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), ), ), ) run(shadowJarTask) - val props = outputShadowJar.getContent(GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH).toProperties() + val props = outputShadowJar.getContent(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR).toProperties() commonAssertions(props) } @@ -60,34 +60,34 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { run(shadowJarTask) - val props = outputShadowJar.getContent(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH).toProperties() + val props = outputShadowJar.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR).toProperties() commonAssertions(props) } private fun buildJarFoo( - path: String = GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH, + path: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, ): Path = buildJar("foo.jar") { insert( path, """ - $MODULE_NAME_KEY=foo - $MODULE_VERSION_KEY=1.0.5 - $EXTENSION_CLASSES_KEY=$EXTENSION_CLASSES_FOO - $STATIC_EXTENSION_CLASSES_KEY=$STATIC_EXTENSION_CLASSES_FOO + $KEY_MODULE_NAME=foo + $KEY_MODULE_VERSION=1.0.5 + $KEY_EXTENSION_CLASSES=$EXTENSION_CLASSES_FOO + $KEY_STATIC_EXTENSION_CLASSES=$STATIC_EXTENSION_CLASSES_FOO """.trimIndent(), ) } private fun buildJarBar( - path: String = GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH, + path: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, ): Path = buildJar("bar.jar") { insert( path, """ - $MODULE_NAME_KEY=bar - $MODULE_VERSION_KEY=2.3.5 - $EXTENSION_CLASSES_KEY=$EXTENSION_CLASSES_BAR - $STATIC_EXTENSION_CLASSES_KEY=$STATIC_EXTENSION_CLASSES_BAR + $KEY_MODULE_NAME=bar + $KEY_MODULE_VERSION=2.3.5 + $KEY_EXTENSION_CLASSES=$EXTENSION_CLASSES_BAR + $KEY_STATIC_EXTENSION_CLASSES=$STATIC_EXTENSION_CLASSES_BAR """.trimIndent(), ) } @@ -99,11 +99,11 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { const val STATIC_EXTENSION_CLASSES_BAR = "com.acme.bar.SomeStaticExtension" fun commonAssertions(properties: Properties) { - assertThat(properties.getProperty(MODULE_NAME_KEY)).isEqualTo(MERGED_MODULE_NAME) - assertThat(properties.getProperty(MODULE_VERSION_KEY)).isEqualTo(MERGED_MODULE_VERSION) - assertThat(properties.getProperty(EXTENSION_CLASSES_KEY)) + assertThat(properties.getProperty(KEY_MODULE_NAME)).isEqualTo(MERGED_MODULE_NAME) + assertThat(properties.getProperty(KEY_MODULE_VERSION)).isEqualTo(MERGED_MODULE_VERSION) + assertThat(properties.getProperty(KEY_EXTENSION_CLASSES)) .isEqualTo("$EXTENSION_CLASSES_FOO,$EXTENSION_CLASSES_BAR") - assertThat(properties.getProperty(STATIC_EXTENSION_CLASSES_KEY)) + assertThat(properties.getProperty(KEY_STATIC_EXTENSION_CLASSES)) .isEqualTo("$STATIC_EXTENSION_CLASSES_FOO,$STATIC_EXTENSION_CLASSES_BAR") } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index 3cfba5754..e001dc5b6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -36,12 +36,12 @@ public open class GroovyExtensionModuleTransformer : Transformer { override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString - if (path == GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH) { + if (path == PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) { // Groovy 2.5+ legacy = false return true } - return path == GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH + return path == PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR } override fun transform(context: TransformerContext) { @@ -49,14 +49,14 @@ public open class GroovyExtensionModuleTransformer : Transformer { props.load(context.inputStream) props.forEach { key, value -> when (key as String) { - MODULE_NAME_KEY -> handle(key, value as String) { + KEY_MODULE_NAME -> handle(key, value as String) { module.setProperty(key, MERGED_MODULE_NAME) } - MODULE_VERSION_KEY -> handle(key, value as String) { + KEY_MODULE_VERSION -> handle(key, value as String) { module.setProperty(key, MERGED_MODULE_VERSION) } - EXTENSION_CLASSES_KEY, - STATIC_EXTENSION_CLASSES_KEY, + KEY_EXTENSION_CLASSES, + KEY_STATIC_EXTENSION_CLASSES, -> handle(key, value as String) { existingValue -> module.setProperty(key, "$existingValue,$value") } @@ -76,7 +76,7 @@ public open class GroovyExtensionModuleTransformer : Transformer { override fun hasTransformedResource(): Boolean = module.isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val name = if (legacy) GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH else GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH + val name = if (legacy) PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR else PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR val entry = ZipEntry(name) entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) @@ -87,14 +87,16 @@ public open class GroovyExtensionModuleTransformer : Transformer { } public companion object { - public const val GROOVY_LEGACY_EXTENSION_MODULE_DESCRIPTOR_PATH: String = + public const val PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR: String = "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" - public const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATH: String = + public const val PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR: String = "META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule" - public const val MODULE_NAME_KEY: String = "moduleName" - public const val MODULE_VERSION_KEY: String = "moduleVersion" - public const val EXTENSION_CLASSES_KEY: String = "extensionClasses" - public const val STATIC_EXTENSION_CLASSES_KEY: String = "staticExtensionClasses" + + public const val KEY_MODULE_NAME: String = "moduleName" + public const val KEY_MODULE_VERSION: String = "moduleVersion" + public const val KEY_EXTENSION_CLASSES: String = "extensionClasses" + public const val KEY_STATIC_EXTENSION_CLASSES: String = "staticExtensionClasses" + public const val MERGED_MODULE_NAME: String = "MergedByShadowJar" public const val MERGED_MODULE_VERSION: String = "1.0.0" } From c2100a83b01a1e48c82a0b3eb76704907c363ba1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 7 Jan 2025 10:51:24 +0800 Subject: [PATCH 125/941] Update plugin spotless to v7.0.0 (#1133) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9112523a9..7a0efb598 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,4 +37,4 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" kotlin = "org.jetbrains.kotlin.jvm:2.1.0" android-lint = "com.android.lint:8.7.3" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" -spotless = "com.diffplug.spotless:7.0.0.BETA4" +spotless = "com.diffplug.spotless:7.0.0" From 710c9fcb9e1cadc753bbcf7345e696ad2f4bace0 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 6 Jan 2025 21:52:40 -0500 Subject: [PATCH 126/941] Migrate functional tests to Kotlin and Junit part 4 (#1131) * Intro Moshi * Migrate XmlSlurper and JsonSlurper to MavenXpp3Reader and Moshi * Convert PublishingSpec * Migrate Moshi to Kotlinx Serialization * Revert "Migrate Moshi to Kotlinx Serialization" This reverts commit 321d8df8edc4e3072dbfc3cfee7ec2f93bd4803d. * Remove unused things in BasePluginSpecification * Corrects * Add documentation reference to GradleModuleMetadata * Add AppendableMavenRepository to replace AppendableMavenFileRepository * Merge AppendableJar into JarBuilder * Remove AppendableMavenRepository in PublishingTest * Optimize performance by publishing common things in BeforeAll * Publish A and B for all tests * Publish C and D for FilteringTest * Build jars in blocks --- build.gradle.kts | 2 + gradle/libs.versions.toml | 3 + .../shadow/BasePluginSpecification.groovy | 104 ++--- .../plugins/shadow/PublishingSpec.groovy | 365 ------------------ .../gradle/plugins/shadow/ApplicationTest.kt | 2 - .../gradle/plugins/shadow/BasePluginTest.kt | 96 +++-- .../plugins/shadow/ConfigurationCacheSpec.kt | 3 - .../gradle/plugins/shadow/FilteringTest.kt | 30 +- .../gradle/plugins/shadow/PublishingTest.kt | 297 ++++++++++++++ .../gradle/plugins/shadow/RelocationTest.kt | 8 +- .../gradle/plugins/shadow/ShadowPluginTest.kt | 74 ++-- .../transformers/BaseTransformerTest.kt | 10 +- .../ServiceFileTransformerTest.kt | 8 +- .../plugins/shadow/util/AppendableJar.kt | 28 -- .../shadow/util/AppendableMavenFileModule.kt | 46 --- .../util/AppendableMavenFileRepository.kt | 11 - .../shadow/util/AppendableMavenRepository.kt | 133 +++++++ .../shadow/util/GradleModuleMetadata.kt | 18 + .../gradle/plugins/shadow/util/JarBuilder.kt | 46 ++- .../shadow/util/repo/AbstractModule.kt | 79 ---- .../util/repo/maven/AbstractMavenModule.kt | 184 --------- .../shadow/util/repo/maven/MavenFileModule.kt | 35 -- .../util/repo/maven/MavenFileRepository.kt | 17 - .../shadow/util/repo/maven/MavenModule.kt | 22 -- .../shadow/util/repo/maven/MavenRepository.kt | 12 - 25 files changed, 633 insertions(+), 1000 deletions(-) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt diff --git a/build.gradle.kts b/build.gradle.kts index 2c951a4d1..4b1a05d86 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -95,6 +95,8 @@ dependencies { intiTestImplementation(libs.apache.maven.repositoryMetadata) // TODO: this will be removed after we migrated all functional tests to Kotlin. intiTestImplementation(sourceSets.main.get().output) + intiTestImplementation(libs.moshi) + intiTestImplementation(libs.moshi.kotlin) lintChecks(libs.androidx.gradlePluginLints) lintChecks(libs.assertk.lint) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7a0efb598..56a9a03e3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] maven = "3.9.9" +moshi = "1.15.2" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" @@ -15,6 +16,8 @@ plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" okio = "com.squareup.okio:okio:3.9.1" +moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } +moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.0" mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:0.30.0" diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy index 8ac09974d..16efb2799 100644 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy +++ b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy @@ -1,19 +1,17 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import com.github.jengelman.gradle.plugins.shadow.util.AppendableJar -import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository +import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import org.apache.commons.lang3.StringUtils -import org.codehaus.plexus.util.IOUtil import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner +import spock.lang.Shared import spock.lang.Specification import spock.lang.TempDir +import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -import java.util.function.Function -import java.util.jar.JarEntry import java.util.jar.JarFile abstract class BasePluginSpecification extends Specification { @@ -21,14 +19,20 @@ abstract class BasePluginSpecification extends Specification { @TempDir Path root - AppendableMavenFileRepository repo + @Shared + static AppendableMavenRepository repo - def setup() { - repo = repo() - repo.module('junit', 'junit', '3.8.2') - .use(Paths.get(this.class.classLoader.getResource('junit-3.8.2.jar').toURI())) - .publish() + def setupSpec() { + repo = new AppendableMavenRepository( + Files.createTempDirectory(null).resolve('local-maven-repo'), + runner, + ) + repo.module('junit', 'junit', '3.8.2') { module -> + module.useJar(Paths.get(this.class.classLoader.getResource('junit-3.8.2.jar').toURI())) + }.publish() + } + def setup() { projectScriptFile << getDefaultProjectBuildScript('java', true, true) settingsScriptFile << getDefaultSettingsBuildScript() } @@ -37,6 +41,10 @@ abstract class BasePluginSpecification extends Specification { println projectScriptFile.text } + def cleanupSpec() { + // TODO: Delete repo recursively. + } + String getDefaultProjectBuildScript( String javaPlugin = 'java', boolean withGroup = false, @@ -61,7 +69,7 @@ abstract class BasePluginSpecification extends Specification { return """ dependencyResolutionManagement { repositories { - maven { url = "${repo.uri}" } + maven { url = "${repo.root.toUri()}" } mavenCentral() } } @@ -73,42 +81,29 @@ abstract class BasePluginSpecification extends Specification { static def shadowJar = "tasks.named('shadowJar', ${ShadowJar.class.name})".trim() GradleRunner getRunner() { - GradleRunner.create() - .withProjectDir(root.toFile()) + def runner = GradleRunner.create() .forwardOutput() .withPluginClasspath() .withTestKitDir(testKitDir) + if (root != null) { + runner.withProjectDir(root.toFile()) + } + return runner } GradleRunner runner(Collection tasks) { runner.withArguments(["--warning-mode=fail", "--configuration-cache", "--stacktrace"] + tasks.toList()) } - BuildResult run(String... tasks) { - run(tasks.toList()) - } - - BuildResult run(List tasks, Function runnerFunction = { it }) { - def result = runnerFunction.apply(runner(tasks)).build() - assertNoDeprecationWarnings(result) - return result - } - - BuildResult runWithFailure(List tasks, Function runnerFunction = { it }) { - def result = runnerFunction.apply(runner(tasks)).buildAndFail() - assertNoDeprecationWarnings(result) - return result - } - - private static void assertNoDeprecationWarnings(BuildResult result) { - result.output.eachLine { - assert !containsDeprecationWarning(it) + BuildResult run(List tasks) { + def result = runner(tasks).build() + result.output.eachLine { output -> + assert !( + output.contains("has been deprecated and is scheduled to be removed in Gradle") || + output.contains("has been deprecated. This is scheduled to be removed in Gradle") + ) } - } - - private static boolean containsDeprecationWarning(String output) { - output.contains("has been deprecated and is scheduled to be removed in Gradle") || - output.contains("has been deprecated. This is scheduled to be removed in Gradle") + return result } File getProjectScriptFile() { @@ -137,20 +132,6 @@ abstract class BasePluginSpecification extends Specification { return f } - AppendableMavenFileRepository repo(String path = 'maven-repo') { - new AppendableMavenFileRepository(root.resolve(path)) - } - - String getJarFileContents(File f, String path) { - JarFile jf = new JarFile(f) - def is = jf.getInputStream(new JarEntry(path)) - StringWriter sw = new StringWriter() - IOUtil.copy(is, sw) - is.close() - jf.close() - return sw.toString() - } - void assertContains(File f, List paths) { JarFile jar = new JarFile(f) paths.each { path -> @@ -167,32 +148,15 @@ abstract class BasePluginSpecification extends Specification { jar.close() } - /** - * Helper method to allow scoping variables into a closure in a spock test - * Prevents variable expansion - * When using this you *must* include explicit `assert` statements as Spock will not do it for you - */ - void assertions(Closure closure) { - closure() - } - - AppendableJar buildJar(String path) { - return new AppendableJar(file(path).toPath()) - } - File getOutputShadowJar() { file('build/libs/shadow-1.0-all.jar') } - static File getTestKitDir() { + private static File getTestKitDir() { def gradleUserHome = System.getenv("GRADLE_USER_HOME") if (!gradleUserHome) { gradleUserHome = new File(System.getProperty("user.home"), ".gradle").absolutePath } return new File(gradleUserHome, "testkit") } - - static String escapedPath(Path path) { - return path.toString().replaceAll('\\\\', '\\\\\\\\') - } } diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy deleted file mode 100644 index 08e9b8170..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/PublishingSpec.groovy +++ /dev/null @@ -1,365 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository -import groovy.json.JsonSlurper -import groovy.xml.XmlSlurper -import org.gradle.api.attributes.Bundling -import org.gradle.api.attributes.Usage -import spock.lang.Issue - -class PublishingSpec extends BasePluginSpecification { - - AppendableMavenFileRepository publishingRepo - - @Override - def setup() { - publishingRepo = repo('remote_repo') - } - - def "publish shadow jar with maven-publish plugin"() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() - - settingsScriptFile << "rootProject.name = 'maven'" - projectScriptFile << """ - apply plugin: 'maven-publish' - - dependencies { - implementation 'shadow:a:1.0' - shadow 'shadow:b:1.0' - } - - $shadowJar { - archiveClassifier = '' - archiveBaseName = 'maven-all' - } - - publishing { - publications { - shadow(MavenPublication) { - from components.shadow - artifactId = 'maven-all' - } - } - repositories { - maven { - url = "${publishingRepo.uri}" - } - } - } - """.stripIndent() - - when: - run('publish') - - then: - File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').toFile().canonicalFile - assert publishedFile.exists() - - and: - assertContains(publishedFile, ['a.properties', 'a2.properties']) - - and: - File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile - assert pom.exists() - - def contents = new XmlSlurper().parse(pom) - assert contents.dependencies.size() == 1 - assert contents.dependencies[0].dependency.size() == 1 - - def dependency = contents.dependencies[0].dependency[0] - assert dependency.groupId.text() == 'shadow' - assert dependency.artifactId.text() == 'b' - assert dependency.version.text() == '1.0' - } - - @Issue([ - "https://github.com/GradleUp/shadow/issues/860", - "https://github.com/GradleUp/shadow/issues/945", - ]) - def "publish shadow jar with maven-publish plugin using custom classifier and extension"() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() - - settingsScriptFile << "rootProject.name = 'maven'" - projectScriptFile << """ - apply plugin: 'maven-publish' - - dependencies { - implementation 'shadow:a:1.0' - shadow 'shadow:b:1.0' - } - - publishing { - publications { - shadow(MavenPublication) { publication -> - project.shadow.component(publication) - artifactId = 'maven-all' - } - } - repositories { - maven { - url = "${publishingRepo.uri}" - } - } - } - - $shadowJar { - archiveClassifier = 'my-classifier' - archiveExtension = 'my-ext' - archiveBaseName = 'maven-all' - } - """.stripIndent() - - when: - run('publish') - - then: - File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext').toFile().canonicalFile - assert publishedFile.exists() - } - - def "publish multiproject shadow jar with maven-publish plugin"() { - given: - - settingsScriptFile << """ - rootProject.name = 'maven' - include 'a' - include 'b' - include 'c' - """.stripMargin() - - projectScriptFile.text = """ - subprojects { - apply plugin: 'java' - apply plugin: 'maven-publish' - - version = "1.0" - group = 'shadow' - - publishing { - repositories { - maven { - url = "${publishingRepo.uri}" - } - } - } - } - """.stripIndent() - - file('a/build.gradle') << """ - plugins { - id 'java' - id 'maven-publish' - } - """.stripMargin() - - file('a/src/main/resources/a.properties') << 'a' - file('a/src/main/resources/a2.properties') << 'a2' - - file('b/build.gradle') << """ - plugins { - id 'java' - id 'maven-publish' - } - """.stripMargin() - - file('b/src/main/resources/b.properties') << 'b' - - file('c/build.gradle') << """ - plugins { - id 'com.gradleup.shadow' - } - - dependencies { - implementation project(':a') - shadow project(':b') - } - - $shadowJar { - archiveClassifier = '' - archiveBaseName = 'maven-all' - } - - publishing { - publications { - shadow(MavenPublication) { - from components.shadow - artifactId = 'maven-all' - } - } - } - """.stripMargin() - - when: - run('publish') - - then: - File publishedFile = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.jar').toFile().canonicalFile - assert publishedFile.exists() - - and: - assertContains(publishedFile, ['a.properties', 'a2.properties']) - - and: - File pom = publishingRepo.rootDir.resolve('shadow/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile - assert pom.exists() - - def contents = new XmlSlurper().parse(pom) - assert contents.dependencies.size() == 1 - assert contents.dependencies[0].dependency.size() == 1 - - def dependency = contents.dependencies[0].dependency[0] - assert dependency.groupId.text() == 'shadow' - assert dependency.artifactId.text() == 'b' - assert dependency.version.text() == '1.0' - } - - def "publish shadow jar with maven-publish plugin and Gradle metadata"() { - given: - repo.module('shadow', 'a', '1.0') - .insertFile('a.properties', 'a') - .insertFile('a2.properties', 'a2') - .publish() - repo.module('shadow', 'b', '1.0') - .insertFile('b.properties', 'b') - .publish() - - settingsScriptFile << """ - rootProject.name = 'maven' - """ - projectScriptFile << """ - apply plugin: 'maven-publish' - dependencies { - implementation 'shadow:a:1.0' - implementation 'shadow:b:1.0' - shadow 'shadow:b:1.0' - } - group = 'com.acme' - version = '1.0' - publishing { - publications { - java(MavenPublication) { - from components.java - } - shadow(MavenPublication) { - from components.shadow - artifactId = "maven-all" - } - } - repositories { - maven { - url = "${publishingRepo.uri}" - } - } - } - """.stripIndent() - - when: - run('publish') - - then: - File mainJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.jar').toFile().canonicalFile - File shadowJar = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0-all.jar').toFile().canonicalFile - assert mainJar.exists() - assert shadowJar.exists() - - and: - assertContains(shadowJar, ['a.properties', 'a2.properties']) - - and: "publishes both a POM file and a Gradle metadata file" - File pom = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.pom').toFile().canonicalFile - File gmm = publishingRepo.rootDir.resolve('com/acme/maven/1.0/maven-1.0.module').toFile().canonicalFile - pom.exists() - gmm.exists() - - when: "POM file corresponds to a regular Java publication" - def pomContents = new XmlSlurper().parse(pom) - pomContents.dependencies.size() == 2 - - then: - def dependency1 = pomContents.dependencies[0].dependency[0] - dependency1.groupId.text() == 'shadow' - dependency1.artifactId.text() == 'a' - dependency1.version.text() == '1.0' - - def dependency2 = pomContents.dependencies[0].dependency[1] - dependency2.groupId.text() == 'shadow' - dependency2.artifactId.text() == 'b' - dependency2.version.text() == '1.0' - - when: "Gradle module metadata contains the Shadow variants" - def gmmContents = new JsonSlurper().parse(gmm) - - then: - gmmContents.variants.size() == 3 - gmmContents.variants.name as Set == ['apiElements', 'runtimeElements', 'shadowRuntimeElements'] as Set - - def apiVariant = gmmContents.variants.find { it.name == 'apiElements' } - apiVariant.attributes[Usage.USAGE_ATTRIBUTE.name] == Usage.JAVA_API - apiVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name] == Bundling.EXTERNAL - !apiVariant.dependencies - - def runtimeVariant = gmmContents.variants.find { it.name == 'runtimeElements' } - runtimeVariant.attributes[Usage.USAGE_ATTRIBUTE.name] == Usage.JAVA_RUNTIME - runtimeVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name] == Bundling.EXTERNAL - runtimeVariant.dependencies.size() == 2 - runtimeVariant.dependencies.module as Set == ['a', 'b'] as Set - - def shadowRuntimeVariant = gmmContents.variants.find { it.name == 'shadowRuntimeElements' } - shadowRuntimeVariant.attributes[Usage.USAGE_ATTRIBUTE.name] == Usage.JAVA_RUNTIME - shadowRuntimeVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name] == Bundling.SHADOWED - shadowRuntimeVariant.dependencies.size() == 1 - shadowRuntimeVariant.dependencies.module as Set == ['b'] as Set - - and: "verify shadow publication" - assertions { - shadowJar = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0-all.jar').toFile().canonicalFile - assert shadowJar.exists() - assertContains(shadowJar, ['a.properties', 'a2.properties']) - } - - assertions { - pom = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.pom').toFile().canonicalFile - assert pom.exists() - pomContents = new XmlSlurper().parse(pom) - assert pomContents.dependencies[0].dependency.size() == 1 - - dependency1 = pomContents.dependencies[0].dependency[0] - assert dependency1.groupId.text() == 'shadow' - assert dependency1.artifactId.text() == 'b' - assert dependency1.version.text() == '1.0' - - dependency2 = pomContents.dependencies[0].dependency[1] - dependency2.groupId.text() == 'shadow' - dependency2.artifactId.text() == 'b' - dependency2.version.text() == '1.0' - } - - assertions { - gmm = publishingRepo.rootDir.resolve('com/acme/maven-all/1.0/maven-all-1.0.module').toFile().canonicalFile - assert gmm.exists() - gmmContents = new JsonSlurper().parse(gmm) - assert gmmContents.variants.size() == 1 - assert gmmContents.variants.name as Set == ['shadowRuntimeElements'] as Set - - runtimeVariant = gmmContents.variants.find { it.name == 'shadowRuntimeElements' } - assert runtimeVariant.attributes[Usage.USAGE_ATTRIBUTE.name] == Usage.JAVA_RUNTIME - assert runtimeVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name] == Bundling.SHADOWED - assert runtimeVariant.dependencies.size() == 1 - assert runtimeVariant.dependencies.module as Set == ['b'] as Set - } - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt index fe67d30ea..2d05cec1a 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt @@ -14,7 +14,6 @@ import org.apache.tools.zip.ZipFile import org.junit.jupiter.api.Test class ApplicationTest : BasePluginTest() { - @Test fun integrationWithApplicationPluginAndJavaToolchains() { prepare( @@ -93,7 +92,6 @@ class ApplicationTest : BasePluginTest() { settingsBlock: String = "", runShadowBlock: String = "", ) { - publishArtifactA() path("src/main/java/myapp/Main.java").appendText( """ package myapp; diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index 2a1319432..ca6282ba4 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -4,7 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Compan import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenFileRepository +import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarPath import java.nio.file.Path import java.util.Properties @@ -24,23 +24,36 @@ import kotlin.io.path.writeText import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.TestInstance @TestInstance(TestInstance.Lifecycle.PER_CLASS) abstract class BasePluginTest { - private lateinit var root: Path - lateinit var repo: AppendableMavenFileRepository + lateinit var root: Path + lateinit var localRepo: AppendableMavenRepository + + @BeforeAll + open fun doFirst() { + localRepo = AppendableMavenRepository(createTempDirectory().resolve("local-maven-repo"), getRunner(null)) + localRepo.module("junit", "junit", "3.8.2") { + useJar(testJar) + }.module("shadow", "a", "1.0") { + buildJar { + insert("a.properties", "a") + insert("a2.properties", "a2") + } + }.module("shadow", "b", "1.0") { + buildJar { + insert("b.properties", "b") + } + }.publish() + } @BeforeEach open fun setup() { root = createTempDirectory() - repo = repo() - repo.module("junit", "junit", "3.8.2") - .use(testJar) - .publish() - projectScriptPath.writeText(getDefaultProjectBuildScript(withGroup = true, withVersion = true)) settingsScriptPath.writeText(getDefaultSettingsBuildScript()) } @@ -56,6 +69,11 @@ abstract class BasePluginTest { println(projectScriptPath.readText()) } + @OptIn(ExperimentalPathApi::class) + fun doLast() { + localRepo.root.deleteRecursively() + } + fun getDefaultProjectBuildScript( javaPlugin: String = "java", withGroup: Boolean = false, @@ -81,7 +99,7 @@ abstract class BasePluginTest { $startBlock dependencyResolutionManagement { repositories { - maven { url = '${repo.uri}' } + maven { url = '${localRepo.root.toUri()}' } mavenCentral() } } @@ -89,30 +107,6 @@ abstract class BasePluginTest { """.trimIndent() + System.lineSeparator() } - fun publishArtifactA() { - repo.module("shadow", "a", "1.0") - .insertFile("a.properties", "a") - .insertFile("a2.properties", "a2") - .publish() - } - - fun publishArtifactB() { - repo.module("shadow", "b", "1.0") - .insertFile("b.properties", "b") - .publish() - } - - fun publishArtifactCD(circular: Boolean = false) { - repo.module("shadow", "c", "1.0") - .insertFile("c.properties", "c") - .apply { if (circular) dependsOn("d") } - .publish() - repo.module("shadow", "d", "1.0") - .insertFile("d.properties", "d") - .dependsOn("c") - .publish() - } - open val shadowJarTask = SHADOW_JAR_TASK_NAME open val runShadowTask = SHADOW_RUN_TASK_NAME val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" @@ -146,26 +140,8 @@ abstract class BasePluginTest { } } - fun repo(path: String = "maven-repo"): AppendableMavenFileRepository { - return AppendableMavenFileRepository(root.resolve(path)) - } - - private val runner: GradleRunner - get() { - return GradleRunner.create() - .withProjectDir(root.toFile()) - .forwardOutput() - .withPluginClasspath() - .withTestKitDir(testKitDir.toFile()) - } - fun runner(arguments: Iterable): GradleRunner { - val allArguments = listOf( - "--warning-mode=fail", - "--configuration-cache", - "--stacktrace", - ) + arguments - return runner.withArguments(allArguments) + return getRunner().withArguments(commonArguments + arguments) } inline fun run( @@ -300,6 +276,16 @@ abstract class BasePluginTest { ) } + private fun getRunner(projectDir: Path? = root) = GradleRunner.create() + .forwardOutput() + .withPluginClasspath() + .withTestKitDir(testKitDir.toFile()) + .apply { + if (projectDir != null) { + withProjectDir(projectDir.toFile()) + } + } + companion object { val testKitDir: Path = run { var gradleUserHome = System.getenv("GRADLE_USER_HOME") @@ -319,6 +305,12 @@ abstract class BasePluginTest { tasks.named('$SHADOW_RUN_TASK_NAME', ${JavaJarExec::class.java.name}) """.trimIndent() + val commonArguments = listOf( + "--warning-mode=fail", + "--configuration-cache", + "--stacktrace", + ) + fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } fun fromJar(vararg paths: Path): String { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt index 79ed273a2..168e4e6a1 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt @@ -14,12 +14,9 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class ConfigurationCacheSpec : BasePluginTest() { - @BeforeEach override fun setup() { super.setup() - publishArtifactA() - publishArtifactB() projectScriptPath.appendText( """ dependencies { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index add31e8e6..d4c5621a9 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -9,17 +9,20 @@ import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class FilteringTest : BasePluginTest() { + @BeforeAll + override fun doFirst() { + super.doFirst() + publishArtifactCD() + } @BeforeEach override fun setup() { super.setup() - publishArtifactA() - publishArtifactB() - projectScriptPath.appendText( """ dependencies { @@ -63,7 +66,6 @@ class FilteringTest : BasePluginTest() { @Test fun excludeDependency() { - publishArtifactCD() dependOnAndExcludeArtifactD() run(shadowJarTask) @@ -73,7 +75,6 @@ class FilteringTest : BasePluginTest() { @Test fun excludeDependencyUsingWildcardSyntax() { - publishArtifactCD() projectScriptPath.appendText( """ dependencies { @@ -94,7 +95,6 @@ class FilteringTest : BasePluginTest() { @Test fun dependencyExclusionsAffectUpToDateCheck() { - publishArtifactCD() dependOnAndExcludeArtifactD() run(shadowJarTask) @@ -121,7 +121,6 @@ class FilteringTest : BasePluginTest() { @Test fun projectExclusionsAffectUpToDateCheck() { - publishArtifactCD() dependOnAndExcludeArtifactD() run(shadowJarTask) @@ -149,7 +148,6 @@ class FilteringTest : BasePluginTest() { @Test fun includeDependencyAndExcludeOthers() { - publishArtifactCD() projectScriptPath.appendText( """ dependencies { @@ -284,4 +282,20 @@ class FilteringTest : BasePluginTest() { "d.properties", ) } + + private fun publishArtifactCD(circular: Boolean = false) { + localRepo.module("shadow", "c", "1.0") { + buildJar { + insert("c.properties", "c") + } + if (circular) { + addDependency("shadow", "d", "1.0") + } + }.module("shadow", "d", "1.0") { + buildJar { + insert("d.properties", "d") + } + addDependency("shadow", "c", "1.0") + }.publish() + } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt new file mode 100644 index 000000000..b0fa6f58e --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -0,0 +1,297 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.all +import assertk.assertThat +import assertk.assertions.containsOnly +import assertk.assertions.isEmpty +import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata +import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.JarPath +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.Moshi +import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory +import java.nio.file.Path +import kotlin.io.path.appendText +import kotlin.io.path.exists +import kotlin.io.path.inputStream +import kotlin.io.path.isRegularFile +import kotlin.io.path.readText +import kotlin.io.path.writeText +import org.apache.maven.model.Model +import org.apache.maven.model.io.xpp3.MavenXpp3Reader +import org.gradle.api.attributes.Bundling +import org.gradle.api.attributes.Usage +import org.gradle.testkit.runner.BuildResult +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class PublishingTest : BasePluginTest() { + private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() + private val gmmAdapter = moshi.adapter(GradleModuleMetadata::class.java) + private val pomReader = MavenXpp3Reader() + + private lateinit var remoteRepo: Path + + @BeforeEach + override fun setup() { + super.setup() + remoteRepo = root.resolve("remote-maven-repo") + settingsScriptPath.appendText("rootProject.name = 'maven'" + System.lineSeparator()) + } + + @Test + fun publishShadowJarWithMavenPublishPlugin() { + projectScriptPath.appendText( + publishConfiguration( + shadowBlock = """ + archiveClassifier = '' + archiveBaseName = 'maven-all' + """.trimIndent(), + ), + ) + + publish() + + assertShadowJarCommon(repoJarPath("shadow/maven-all/1.0/maven-all-1.0.jar")) + assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) + } + + @Issue( + "https://github.com/GradleUp/shadow/issues/860", + "https://github.com/GradleUp/shadow/issues/945", + ) + @Test + fun publishShadowJarWithCustomClassifierAndExtension() { + projectScriptPath.appendText( + publishConfiguration( + shadowBlock = """ + archiveClassifier = 'my-classifier' + archiveExtension = 'my-ext' + archiveBaseName = 'maven-all' + """.trimIndent(), + ), + ) + + publish() + + assertShadowJarCommon(repoJarPath("shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext")) + assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) + } + + @Test + fun publishMultiProjectShadowJarWithMavenPublishPlugin() { + settingsScriptPath.appendText( + """ + include 'a', 'b', 'c' + """.trimIndent(), + ) + projectScriptPath.writeText( + """ + subprojects { + apply plugin: 'java' + apply plugin: 'maven-publish' + version = '1.0' + group = 'shadow' + } + """.trimIndent(), + ) + + path("a/src/main/resources/aa.properties").writeText("aa") + path("a/src/main/resources/aa2.properties").writeText("aa2") + path("b/src/main/resources/bb.properties").writeText("bb") + + val publishBlock = publishConfiguration( + dependenciesBlock = """ + implementation project(':a') + shadow project(':b') + """.trimIndent(), + shadowBlock = """ + archiveClassifier = '' + archiveBaseName = 'maven-all' + """.trimIndent(), + ) + path("c/build.gradle").writeText( + """ + ${getDefaultProjectBuildScript(withGroup = true, withVersion = true)} + $publishBlock + """.trimIndent(), + ) + + publish() + + val publishedJar = repoJarPath("shadow/maven-all/1.0/maven-all-1.0.jar") + assertThat(publishedJar).containsEntries( + "aa.properties", + "aa2.properties", + ) + assertThat(publishedJar).doesNotContainEntries( + "a.properties", + "a2.properties", + "b.properties", + "bb.properties", + ) + + assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) + } + + @Test + fun publishShadowJarWithGradleMetadata() { + projectScriptPath.appendText( + publishConfiguration( + projectBlock = """ + group = 'com.acme' + version = '1.0' + """.trimIndent(), + dependenciesBlock = """ + implementation 'shadow:a:1.0' + implementation 'shadow:b:1.0' + shadow 'shadow:b:1.0' + """.trimIndent(), + publicationsBlock = """ + java(MavenPublication) { + from components.java + } + shadow(MavenPublication) { + from components.shadow + artifactId = "maven-all" + } + """.trimIndent(), + ), + ) + + publish() + + val entries = arrayOf("a.properties", "a2.properties", "b.properties") + assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0.jar")).doesNotContainEntries(*entries) + assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0-all.jar")).containsEntries(*entries) + + pomReader.read(repoPath("com/acme/maven/1.0/maven-1.0.pom")).let { pomContents -> + assertThat(pomContents.dependencies.size).isEqualTo(2) + pomContents.dependencies[0].let { dependency -> + assertThat(dependency.groupId).isEqualTo("shadow") + assertThat(dependency.artifactId).isEqualTo("a") + assertThat(dependency.version).isEqualTo("1.0") + } + pomContents.dependencies[1].let { dependency -> + assertThat(dependency.groupId).isEqualTo("shadow") + assertThat(dependency.artifactId).isEqualTo("b") + assertThat(dependency.version).isEqualTo("1.0") + } + } + + gmmAdapter.fromJson(repoPath("com/acme/maven/1.0/maven-1.0.module")).let { gmmContents -> + assertThat(gmmContents.variants.size).isEqualTo(3) + assertThat(gmmContents.variants.map { it.name }).containsOnly( + "apiElements", + "runtimeElements", + "shadowRuntimeElements", + ) + + val apiVariant = gmmContents.variants.single { it.name == "apiElements" } + assertThat(apiVariant.attributes[Usage.USAGE_ATTRIBUTE.name]).isEqualTo(Usage.JAVA_API) + assertThat(apiVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name]).isEqualTo(Bundling.EXTERNAL) + assertThat(apiVariant.dependencies).isEmpty() + + val runtimeVariant = gmmContents.variants.single { it.name == "runtimeElements" } + assertThat(runtimeVariant.attributes[Usage.USAGE_ATTRIBUTE.name]).isEqualTo(Usage.JAVA_RUNTIME) + assertThat(runtimeVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name]).isEqualTo(Bundling.EXTERNAL) + assertThat(runtimeVariant.dependencies.size).isEqualTo(2) + assertThat(runtimeVariant.dependencies.map { it.module }).containsOnly("a", "b") + + assertShadowVariantCommon(gmmContents.variants.single { it.name == "shadowRuntimeElements" }) + } + assertPomCommon(repoPath("com/acme/maven-all/1.0/maven-all-1.0.pom")) + + gmmAdapter.fromJson(repoPath("com/acme/maven-all/1.0/maven-all-1.0.module")).let { gmmContents -> + assertThat(gmmContents.variants.size).isEqualTo(1) + assertShadowVariantCommon(gmmContents.variants.single { it.name == "shadowRuntimeElements" }) + } + } + + private fun repoPath(path: String): Path { + return remoteRepo.resolve(path).also { + check(it.exists()) { "Path not found: $it" } + check(it.isRegularFile()) { "Path is not a regular file: $it" } + } + } + + private fun repoJarPath(path: String): JarPath { + return JarPath(repoPath(path)) + } + + private fun publish(): BuildResult = run("publish") + + private fun publishConfiguration( + projectBlock: String = "", + dependenciesBlock: String = """ + implementation 'shadow:a:1.0' + shadow 'shadow:b:1.0' + """.trimIndent(), + shadowBlock: String = "", + publicationsBlock: String = """ + shadow(MavenPublication) { + from components.shadow + artifactId = 'maven-all' + } + """.trimIndent(), + ): String { + return """ + apply plugin: 'maven-publish' + $projectBlock + dependencies { + $dependenciesBlock + } + $shadowJar { + $shadowBlock + } + publishing { + publications { + $publicationsBlock + } + repositories { + maven { + url = '${remoteRepo.toUri()}' + } + } + } + """.trimIndent() + } + + private fun assertPomCommon(pomPath: Path) { + val contents = pomReader.read(pomPath) + assertThat(contents.dependencies.size).isEqualTo(1) + + val dependency = contents.dependencies[0] + assertThat(dependency.groupId).isEqualTo("shadow") + assertThat(dependency.artifactId).isEqualTo("b") + assertThat(dependency.version).isEqualTo("1.0") + } + + private fun assertShadowVariantCommon(variant: GradleModuleMetadata.Variant) { + assertThat(variant.attributes[Usage.USAGE_ATTRIBUTE.name]).isEqualTo(Usage.JAVA_RUNTIME) + assertThat(variant.attributes[Bundling.BUNDLING_ATTRIBUTE.name]).isEqualTo(Bundling.SHADOWED) + assertThat(variant.dependencies.size).isEqualTo(1) + assertThat(variant.dependencies.map { it.module }).containsOnly("b") + } + + private fun assertShadowJarCommon(jarPath: JarPath) { + assertThat(jarPath).all { + containsEntries( + "a.properties", + "a2.properties", + ) + doesNotContainEntries( + "b.properties", + ) + } + } + + private companion object { + fun MavenXpp3Reader.read(path: Path): Model = read(path.inputStream()) + + fun JsonAdapter.fromJson(path: Path): T = requireNotNull(fromJson(path.readText())) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 3a5038030..e79716bd6 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -288,9 +288,11 @@ class RelocationTest : BasePluginTest() { ) @Test fun relocateResourceFiles() { - repo.module("shadow", "dep", "1.0") - .insertFile("foo/dep.properties", "c") - .publish() + localRepo.module("shadow", "dep", "1.0") { + buildJar { + insert("foo/dep.properties", "c") + } + }.publish() path("src/main/java/foo/Foo.java").writeText( """ package foo; diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt index 67f71634e..d64ea99fa 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -382,15 +382,17 @@ class ShadowPluginTest : BasePluginTest() { @Test fun excludeSomeMetaInfFilesByDefault() { - repo.module("shadow", "a", "1.0") - .insertFile("a.properties", "a") - .insertFile("META-INF/INDEX.LIST", "JarIndex-Version: 1.0") - .insertFile("META-INF/a.SF", "Signature File") - .insertFile("META-INF/a.DSA", "DSA Signature Block") - .insertFile("META-INF/a.RSA", "RSA Signature Block") - .insertFile("META-INF/a.properties", "key=value") - .insertFile("module-info.class", "module myModuleName {}") - .publish() + localRepo.module("shadow", "a", "1.0") { + buildJar { + insert("a.properties", "a") + insert("META-INF/INDEX.LIST", "JarIndex-Version: 1.0") + insert("META-INF/a.SF", "Signature File") + insert("META-INF/a.DSA", "DSA Signature Block") + insert("META-INF/a.RSA", "RSA Signature Block") + insert("META-INF/a.properties", "key=value") + insert("module-info.class", "module myModuleName {}") + } + }.publish() path("src/main/java/shadow/Passed.java").writeText( """ @@ -424,9 +426,6 @@ class ShadowPluginTest : BasePluginTest() { @Test fun includeRuntimeConfigurationByDefault() { - publishArtifactA() - publishArtifactB() - projectScriptPath.appendText( """ dependencies { @@ -449,19 +448,24 @@ class ShadowPluginTest : BasePluginTest() { @Test fun includeJavaLibraryConfigurationsByDefault() { - repo.module("shadow", "api", "1.0") - .insertFile("api.properties", "api") - .publish() - repo.module("shadow", "implementation-dep", "1.0") - .insertFile("implementation-dep.properties", "implementation-dep") - .publish() - repo.module("shadow", "implementation", "1.0") - .insertFile("implementation.properties", "implementation") - .dependsOn("implementation-dep") - .publish() - repo.module("shadow", "runtimeOnly", "1.0") - .insertFile("runtimeOnly.properties", "runtimeOnly") - .publish() + localRepo.module("shadow", "api", "1.0") { + buildJar { + insert("api.properties", "api") + } + }.module("shadow", "implementation-dep", "1.0") { + buildJar { + insert("implementation-dep.properties", "implementation-dep") + } + }.module("shadow", "implementation", "1.0") { + buildJar { + insert("implementation.properties", "implementation") + } + addDependency("shadow", "implementation-dep", "1.0") + }.module("shadow", "runtimeOnly", "1.0") { + buildJar { + insert("runtimeOnly.properties", "runtimeOnly") + } + }.publish() projectScriptPath.writeText( """ @@ -486,9 +490,6 @@ class ShadowPluginTest : BasePluginTest() { @Test fun doesNotIncludeCompileOnlyConfigurationByDefault() { - publishArtifactA() - publishArtifactB() - projectScriptPath.appendText( """ dependencies { @@ -511,12 +512,15 @@ class ShadowPluginTest : BasePluginTest() { @Test fun defaultCopyingStrategy() { - repo.module("shadow", "a", "1.0") - .insertFile("META-INF/MANIFEST.MF", "MANIFEST A") - .publish() - repo.module("shadow", "b", "1.0") - .insertFile("META-INF/MANIFEST.MF", "MANIFEST B") - .publish() + localRepo.module("shadow", "a", "1.0") { + buildJar { + insert("META-INF/MANIFEST.MF", "MANIFEST A") + } + }.module("shadow", "b", "1.0") { + buildJar { + insert("META-INF/MANIFEST.MF", "MANIFEST B") + } + }.publish() projectScriptPath.appendText( """ @@ -621,8 +625,6 @@ class ShadowPluginTest : BasePluginTest() { @Disabled @Test fun checkLargeZipFilesWithZip64Enabled() { - publishArtifactA() - path("src/main/java/myapp/Main.java").writeText( """ package myapp; diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index c38ad8a12..7f09688a6 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -1,14 +1,14 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.BasePluginTest -import com.github.jengelman.gradle.plugins.shadow.util.AppendableJar +import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder import java.nio.file.Path import kotlin.io.path.writeText sealed class BaseTransformerTest : BasePluginTest() { fun buildJarOne( - builder: AppendableJar.() -> Unit = { + builder: JarBuilder.() -> Unit = { insert(ENTRY_SERVICES_SHADE, CONTENT_ONE) insert(ENTRY_SERVICES_FOO, "one") }, @@ -17,7 +17,7 @@ sealed class BaseTransformerTest : BasePluginTest() { } fun buildJarTwo( - builder: AppendableJar.() -> Unit = { + builder: JarBuilder.() -> Unit = { insert(ENTRY_SERVICES_SHADE, CONTENT_TWO) insert(ENTRY_SERVICES_FOO, "two") }, @@ -25,8 +25,8 @@ sealed class BaseTransformerTest : BasePluginTest() { return buildJar("two.jar", builder) } - inline fun buildJar(path: String, builder: AppendableJar.() -> Unit): Path { - return AppendableJar(path(path)).apply(builder).write() + inline fun buildJar(path: String, builder: JarBuilder.() -> Unit): Path { + return JarBuilder(path(path)).apply(builder).write() } fun writeMainClass() { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 00de08e5a..65923952a 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -178,9 +178,11 @@ class ServiceFileTransformerTest : BaseTransformerTest() { val one = buildJarOne { insert(servicesShadowEntry, CONTENT_ONE) }.toUri().toURL().path - repo.module("shadow", "two", "1.0") - .insertFile(servicesShadowEntry, CONTENT_TWO) - .publish() + localRepo.module("shadow", "two", "1.0") { + buildJar { + insert(servicesShadowEntry, CONTENT_TWO) + } + }.publish() projectScriptPath.appendText( """ diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt deleted file mode 100644 index 41b102d82..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableJar.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import java.io.OutputStream -import java.nio.file.Path -import kotlin.io.path.outputStream - -class AppendableJar(private val outputPath: Path) { - private val contents = mutableMapOf() - - fun insert(path: String, content: String): AppendableJar = apply { - contents[path] = content - } - - fun write(): Path { - write(contents, outputPath.outputStream()) - return outputPath - } - - companion object { - fun write(contents: Map, outputStream: OutputStream) { - val builder = JarBuilder(outputStream) - contents.forEach { (path, content) -> - builder.withPath(path, content) - } - builder.build() - } - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt deleted file mode 100644 index d41213392..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileModule.kt +++ /dev/null @@ -1,46 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileModule -import java.nio.file.Path -import kotlin.io.path.inputStream - -class AppendableMavenFileModule(module: MavenFileModule) : MavenFileModule(module.moduleDir, module.groupId, module.artifactId, module.version) { - - private val contents = mutableMapOf>().withDefault { mutableMapOf() } - private val paths = mutableMapOf() - - fun use(path: Path): AppendableMavenFileModule { - return use("", path) - } - - fun use(classifier: String, path: Path): AppendableMavenFileModule = apply { - paths[classifier] = path - } - - fun insertFile(path: String, content: String): AppendableMavenFileModule { - return insertFile("", path, content) - } - - fun insertFile(classifier: String, path: String, content: String): AppendableMavenFileModule = apply { - contents.getOrPut(classifier) { mutableMapOf() }[path] = content - } - - override fun publishArtifact(artifact: Map): Path { - val artifactPath = artifactPath(artifact) - if (type == "pom") { - return artifactPath - } - val classifier = artifact["classifier"] as? String ?: "" - val classifierPath = paths[classifier] - if (classifierPath != null) { - publish(artifactPath) { os -> - classifierPath.inputStream().copyTo(os) - } - } else { - publish(artifactPath) { os -> - AppendableJar.write(contents[classifier].orEmpty(), os) - } - } - return artifactPath - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt deleted file mode 100644 index e2ce40630..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenFileRepository.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import com.github.jengelman.gradle.plugins.shadow.util.repo.maven.MavenFileRepository -import java.nio.file.Path - -class AppendableMavenFileRepository(rootDir: Path) : MavenFileRepository(rootDir) { - - override fun module(groupId: String, artifactId: String, version: String): AppendableMavenFileModule { - return AppendableMavenFileModule(super.module(groupId, artifactId, version)) - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt new file mode 100644 index 000000000..5786ec8bd --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -0,0 +1,133 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import com.github.jengelman.gradle.plugins.shadow.BasePluginTest.Companion.commonArguments +import java.nio.file.Path +import kotlin.io.path.createDirectories +import kotlin.io.path.createFile +import kotlin.io.path.exists +import kotlin.io.path.isRegularFile +import kotlin.io.path.name +import kotlin.io.path.writeText +import org.apache.maven.model.Dependency +import org.apache.maven.model.Model +import org.gradle.testkit.runner.GradleRunner + +class AppendableMavenRepository( + val root: Path, + private val gradleRunner: GradleRunner, +) { + private val projectBuildScript: Path + private val modules = mutableListOf() + + init { + root.createDirectories() + root.resolve("settings.gradle").createFile() + .writeText("rootProject.name = '${root.name}'") + projectBuildScript = root.resolve("build.gradle").createFile() + } + + fun module( + groupId: String, + artifactId: String, + version: String, + action: Module.() -> Unit, + ) = apply { + modules += Module(groupId, artifactId, version).also(action) + } + + fun publish() { + if (modules.isEmpty()) return + projectBuildScript.writeText( + """ + plugins { + id 'maven-publish' + } + publishing { + publications { + ${modules.joinToString(System.lineSeparator()) { createPublication(it) }} + } + repositories { + maven { + url = '${root.toUri()}' + } + } + } + """.trimIndent(), + ) + gradleRunner.withProjectDir(root.toFile()).withArguments(commonArguments + "publish").build() + modules.clear() + } + + private fun createPublication(module: Module) = with(module) { + val outputJar = build() + val pubName = outputJar.name.replace(".", "") + + var index = -1 + val nodes = dependencies.joinToString(System.lineSeparator()) { + index++ + val node = "dependencyNode$index" + """ + def $node = dependenciesNode.appendNode('dependency') + $node.appendNode('groupId', '${it.groupId}') + $node.appendNode('artifactId', '${it.artifactId}') + $node.appendNode('version', '${it.version}') + $node.appendNode('scope', '${it.scope}') + """.trimIndent() + } + + """ + create('$pubName', MavenPublication) { + artifactId = '$artifactId' + groupId = '$groupId' + version = '$version' + artifact '${outputJar.toUri().toURL().path}' + pom.withXml { xml -> + def dependenciesNode = xml.asNode().get('dependencies') ?: xml.asNode().appendNode('dependencies') + $nodes + } + } + """.trimIndent() + System.lineSeparator() + } + + inner class Module( + groupId: String, + artifactId: String, + version: String, + ) : Model() { + private val coordinate = "$groupId:$artifactId:$version" + private lateinit var existingJar: Path + + init { + this.groupId = groupId + this.artifactId = artifactId + this.version = version + } + + fun useJar(existingJar: Path) { + this.existingJar = existingJar + } + + fun buildJar(builder: JarBuilder.() -> Unit) { + val jarName = coordinate.replace(":", "-") + ".jar" + existingJar = JarBuilder(root.resolve(jarName)).apply(builder).write() + } + + fun addDependency(groupId: String, artifactId: String, version: String, scope: String = "runtime") { + val dependency = Dependency().also { + it.groupId = groupId + it.artifactId = artifactId + it.version = version + it.scope = scope + } + addDependency(dependency) + } + + fun build(): Path { + if (!::existingJar.isInitialized) error("No jar file provided for $coordinate") + return existingJar.also { + check(it.exists()) { "Jar file doesn't exist for $coordinate in: $it" } + check(it.isRegularFile()) { "Jar is not a regular file for $coordinate in: $it" } + } + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt new file mode 100644 index 000000000..13c95b32a --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt @@ -0,0 +1,18 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +/** + * Refs the format from [Gradle Module Metadata](https://github.com/gradle/gradle/blob/master/platforms/documentation/docs/src/docs/design/gradle-module-metadata-latest-specification.md). + */ +data class GradleModuleMetadata( + val variants: List, +) { + data class Variant( + val name: String, + val attributes: Map, + val dependencies: List = emptyList(), + ) { + data class Dependency( + val module: String, + ) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt index 2372d5823..d59675a6c 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt @@ -1,12 +1,36 @@ package com.github.jengelman.gradle.plugins.shadow.util -import java.io.OutputStream +import java.nio.file.Path import java.util.jar.JarEntry import java.util.jar.JarOutputStream +import kotlin.io.path.outputStream -class JarBuilder(os: OutputStream) { +class JarBuilder( + private val outputPath: Path, +) { + private val contents = mutableMapOf() private val entries = mutableSetOf() - private val jos = JarOutputStream(os) + private val jos = JarOutputStream(outputPath.outputStream()) + + fun insert(path: String, content: String): JarBuilder = apply { + contents[path] = content + } + + fun write(): Path { + jos.use { + contents.forEach { (entry, content) -> + val idx = entry.lastIndexOf('/') + if (idx != -1) { + addDirectory(entry.substring(0, idx)) + } + if (entries.add(entry)) { + jos.putNextEntry(JarEntry(entry)) + content.byteInputStream().copyTo(jos) + } + } + } + return outputPath + } private fun addDirectory(name: String) { if (entries.add(name)) { @@ -18,20 +42,4 @@ class JarBuilder(os: OutputStream) { jos.putNextEntry(JarEntry("$name/")) } } - - fun withPath(path: String, data: String): JarBuilder { - val idx = path.lastIndexOf('/') - if (idx != -1) { - addDirectory(path.substring(0, idx)) - } - if (entries.add(path)) { - jos.putNextEntry(JarEntry(path)) - data.byteInputStream().copyTo(jos) - } - return this - } - - fun build() { - jos.close() - } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt deleted file mode 100644 index 827964aa4..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/AbstractModule.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo - -import java.io.OutputStream -import java.io.UncheckedIOException -import java.math.BigInteger -import java.nio.file.Path -import kotlin.io.path.absolutePathString -import kotlin.io.path.exists -import kotlin.io.path.moveTo -import kotlin.io.path.name -import kotlin.io.path.outputStream -import kotlin.io.path.readBytes -import kotlin.io.path.writeText -import okio.ByteString.Companion.toByteString - -abstract class AbstractModule { - - protected open fun postPublish(path: Path) = Unit - - protected fun publish(path: Path, action: (OutputStream) -> Unit) { - val hashBefore = if (path.exists()) getHash(path, "sha1") else null - val tempPath = path.resolveSibling("${path.name}.tmp") - tempPath.outputStream().use(action) - - val hashAfter = getHash(tempPath, "sha1") - if (hashAfter == hashBefore) { - // Already published - return - } - - tempPath.moveTo(path, true) - check(path.exists()) - writeSha1Path(path) - writeMd5Path(path) - - postPublish(path) - } - - companion object { - fun writeSha1Path(path: Path): Path { - return writeHashPath(path, "sha1", 40) - } - - fun writeMd5Path(path: Path): Path { - return writeHashPath(path, "md5", 32) - } - - private fun writeHashPath(path: Path, algorithm: String, len: Int): Path { - val hashPath = getHashPath(path, algorithm) - val hash = getHash(path, algorithm) - hashPath.writeText("$hash${len}x") - return hashPath - } - - private fun getHashPath(path: Path, algorithm: String): Path { - return path.resolveSibling("${path.name}.$algorithm") - } - - private fun getHash(path: Path, algorithm: String): BigInteger { - try { - val byteString = path.readBytes().toByteString() - val byteArray = when (algorithm.uppercase()) { - "MD5" -> byteString.md5() - "SHA1" -> byteString.sha1() - "SHA256" -> byteString.sha256() - "SHA512" -> byteString.sha512() - else -> throw IllegalArgumentException("Unsupported algorithm: $algorithm") - }.toByteArray() - return BigInteger(1, byteArray) - } catch (e: UncheckedIOException) { - // Catch any unchecked io exceptions and add the file path for troubleshooting - throw UncheckedIOException( - "Failed to create $algorithm hash for file ${path.absolutePathString()}.", - e.cause, - ) - } - } - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt deleted file mode 100644 index 8d4ada5d6..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/AbstractMavenModule.kt +++ /dev/null @@ -1,184 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import com.github.jengelman.gradle.plugins.shadow.util.repo.AbstractModule -import java.nio.file.Path -import java.text.SimpleDateFormat -import java.util.Date -import kotlin.io.path.createDirectories -import kotlin.io.path.exists -import kotlin.io.path.isRegularFile -import kotlin.io.path.name -import kotlin.io.path.reader -import org.apache.maven.artifact.repository.metadata.Metadata -import org.apache.maven.artifact.repository.metadata.Snapshot -import org.apache.maven.artifact.repository.metadata.Versioning -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Reader -import org.apache.maven.artifact.repository.metadata.io.xpp3.MetadataXpp3Writer -import org.apache.maven.model.Dependency -import org.apache.maven.model.Model -import org.apache.maven.model.io.xpp3.MavenXpp3Writer - -abstract class AbstractMavenModule( - val moduleDir: Path, - val groupId: String, - val artifactId: String, - val version: String, -) : AbstractModule(), - MavenModule { - - protected val updateFormat = SimpleDateFormat("yyyyMMddHHmmss") - protected val timestampFormat = SimpleDateFormat("yyyyMMdd.HHmmss") - protected val dependencies = mutableListOf() - protected val artifacts = mutableListOf>() - - protected var type: String = "jar" - protected var packaging: String? = null - protected var publishCount: Int = 1 - - protected abstract val isUniqueSnapshots: Boolean - protected abstract val isPublishesMetaDataFile: Boolean - - fun dependsOn(artifactId: String): MavenModule { - return dependsOn(groupId = groupId, artifactId = artifactId, version = version) - } - - override fun dependsOn(groupId: String, artifactId: String, version: String): MavenModule = apply { - val dep = Dependency().also { - it.groupId = groupId - it.artifactId = artifactId - it.version = version - } - dependencies.add(dep) - } - - override val pomPath: Path - get() = moduleDir.resolve("$artifactId-$publishArtifactVersion.pom") - - override val metaDataPath: Path - get() = moduleDir.resolve(MAVEN_METADATA_FILE) - - val rootMetaDataPath: Path - get() = moduleDir.resolveSibling(MAVEN_METADATA_FILE) - - fun artifactPath(options: Map): Path { - val artifact = toArtifact(options) - var fileName = "$artifactId-$publishArtifactVersion.${artifact["type"]}" - if (artifact["classifier"] != null) { - fileName = "$artifactId-$publishArtifactVersion-${artifact["classifier"]}.${artifact["type"]}" - } - return moduleDir.resolve(fileName) - } - - override fun publishPom(): MavenModule = apply { - moduleDir.createDirectories() - val rootMavenMetaData = rootMetaDataPath - updateRootMavenMetaData(rootMavenMetaData) - - if (isPublishesMetaDataFile) { - publish(metaDataPath) { outputStream -> - MetadataXpp3Writer().write(outputStream, getMetaData(emptyList())) - } - } - - publish(pomPath) { outputStream -> - val pomPackaging = packaging ?: type - val model = Model().also { - it.modelVersion = "4.0.0" - it.groupId = groupId - it.artifactId = artifactId - it.version = version - it.packaging = pomPackaging - it.description = "Published on $publishTimestamp" - it.dependencies = dependencies - } - MavenXpp3Writer().write(outputStream, model) - } - } - - open fun getMetaData(versions: List): Metadata = Metadata().also { - it.groupId = groupId - it.artifactId = artifactId - it.version = version - it.versioning = Versioning().also { versioning -> - versioning.versions = versions - versioning.lastUpdated = updateFormat.format(publishTimestamp) - if (isUniqueSnapshots && version.endsWith("-SNAPSHOT")) { - versioning.snapshot = Snapshot().apply { - timestamp = timestampFormat.format(publishTimestamp) - buildNumber = publishCount - } - } - } - } - - override fun publish(): MavenModule = apply { - publishPom() - artifacts.forEach { artifact -> - publishArtifact(artifact) - } - publishArtifact(emptyMap()) - } - - open fun publishArtifact(artifact: Map): Path { - val artifactPath = artifactPath(artifact) - if (type == "pom") { - return artifactPath - } - publish(artifactPath) { outputStream -> - outputStream.write("${artifactPath.name} : $artifactContent".toByteArray()) - } - return artifactPath - } - - protected fun toArtifact(options: Map): Map { - val artifact = mutableMapOf( - "type" to (options["type"] ?: type), - "classifier" to options["classifier"], - ) - require(options.keys.isEmpty()) { "Unknown options : ${options.keys}" } - return artifact - } - - protected val publishArtifactVersion: String - get() = if (isUniqueSnapshots && version.endsWith("-SNAPSHOT")) { - "${version.removeSuffix("-SNAPSHOT")}-$uniqueSnapshotVersion" - } else { - version - } - - protected val publishTimestamp: Date - get() = Date(updateFormat.parse("20100101120000").time + publishCount * 1000) - - private fun updateRootMavenMetaData(rootMavenMetaData: Path) { - val allVersions = if (rootMavenMetaData.exists()) { - MetadataXpp3Reader().read(rootMavenMetaData.reader()).versioning.versions - } else { - mutableListOf() - } - allVersions.add(version) - publish(rootMavenMetaData) { outputStream -> - MetadataXpp3Writer().write(outputStream, getMetaData(allVersions)) - } - } - - private val artifactContent: String - // Some content to include in each artifact, so that its size and content varies on each pu - get() = (0..publishCount).joinToString("-") - - private val uniqueSnapshotVersion: String - get() { - require(isUniqueSnapshots && version.endsWith("-SNAPSHOT")) - return if (metaDataPath.isRegularFile()) { - val metaData = MetadataXpp3Reader().read(metaDataPath.reader()) - val timestamp = metaData.versioning.snapshot.timestamp - val build = metaData.versioning.snapshot.buildNumber - "$timestamp-$build" - } else { - "${timestampFormat.format(publishTimestamp)}-$publishCount" - } - } - - protected companion object { - const val MAVEN_METADATA_FILE = "maven-metadata.xml" - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt deleted file mode 100644 index 0807d4021..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileModule.kt +++ /dev/null @@ -1,35 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import java.nio.file.Path -import org.apache.maven.artifact.repository.metadata.Metadata -import org.apache.maven.artifact.repository.metadata.Snapshot -import org.apache.maven.artifact.repository.metadata.Versioning - -open class MavenFileModule( - moduleDir: Path, - groupId: String, - artifactId: String, - version: String, -) : AbstractMavenModule(moduleDir, groupId, artifactId, version) { - - override val isUniqueSnapshots: Boolean = true - - override fun getMetaData(versions: List): Metadata { - return Metadata().also { - it.groupId = groupId - it.artifactId = artifactId - it.version = version - it.versioning = Versioning().also { versioning -> - versioning.versions = versions - versioning.snapshot = Snapshot().apply { - timestamp = timestampFormat.format(publishTimestamp) - buildNumber = publishCount - } - versioning.lastUpdated = updateFormat.format(publishTimestamp) - } - } - } - - override val isPublishesMetaDataFile: Boolean - get() = isUniqueSnapshots && version.endsWith("-SNAPSHOT") -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt deleted file mode 100644 index e1ba0c089..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenFileRepository.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import java.net.URI -import java.nio.file.Path - -/** - * A fixture for dealing with file Maven repositories. - */ -open class MavenFileRepository(val rootDir: Path) : MavenRepository { - - override val uri: URI = rootDir.toUri() - - override fun module(groupId: String, artifactId: String, version: String): MavenFileModule { - val artifactDir = rootDir.resolve("${groupId.replace('.', '/')}/$artifactId/$version") - return MavenFileModule(artifactDir, groupId, artifactId, version) - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt deleted file mode 100644 index e484f2a96..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenModule.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import java.nio.file.Path - -interface MavenModule { - /** - * Publishes the `pom.xml` plus main artifact, plus any additional artifacts for this module. - * Publishes only those artifacts whose content has changed since the last call to `publish()`. - */ - fun publish(): MavenModule - - /** - * Publishes the `pom.xml` only - */ - fun publishPom(): MavenModule - - fun dependsOn(groupId: String, artifactId: String, version: String): MavenModule - - val pomPath: Path - - val metaDataPath: Path -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt deleted file mode 100644 index 95ca83c86..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/repo/maven/MavenRepository.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util.repo.maven - -import java.net.URI - -/** - * A fixture for dealing with Maven repositories. - */ -interface MavenRepository { - val uri: URI - - fun module(groupId: String, artifactId: String, version: String): MavenModule -} From 2377f1730cefb4a5319def0b92621324d5b3f850 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 22:56:33 +0800 Subject: [PATCH 127/941] Update plugin spotless to v7.0.1 (#1135) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 56a9a03e3..40b471659 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -40,4 +40,4 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" kotlin = "org.jetbrains.kotlin.jvm:2.1.0" android-lint = "com.android.lint:8.7.3" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" -spotless = "com.diffplug.spotless:7.0.0" +spotless = "com.diffplug.spotless:7.0.1" From 1424c6063d099737e53dda08bd12a3610f298357 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 8 Jan 2025 22:56:50 +0800 Subject: [PATCH 128/941] Update dependency com.squareup.okio:okio to v3.10.1 (#1136) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 40b471659..50750207c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" -okio = "com.squareup.okio:okio:3.9.1" +okio = "com.squareup.okio:okio:3.10.1" moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } From f1b91611d1c98ecbef62a9469183cbb06995f465 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Jan 2025 10:09:14 +0800 Subject: [PATCH 129/941] Update dependency com.squareup.okio:okio to v3.10.2 (#1137) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 50750207c..5050e5492 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -15,7 +15,7 @@ jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" -okio = "com.squareup.okio:okio:3.10.1" +okio = "com.squareup.okio:okio:3.10.2" moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } From 432412de4620b40810d5970c23a59de0864e7e15 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 9 Jan 2025 00:05:22 -0500 Subject: [PATCH 130/941] Migrate functional tests to Kotlin and Junit part 5 (#1134) * Rename containsEntries and assertDoesNotContain * Add BaseCachingTest * Convert MinimizationCachingSpec * Convert RelocationCachingSpec * Rename ConfigurationCacheSpec * Convert ShadowJarCachingSpec * Clean up TransformCachingSpec * Convert TransformCachingSpec * Remove AbstractCachingSpec * Replace transform blocks * Cleanups * Reuse writeMainClass * Move transform kit to BasePluginTest * Ruse runner and run * Try to cache JarPath * Revert "Try to cache JarPath" This reverts commit 19dfd90f469d1fd8ce57465e8b3b545242faf906. * Close reader in getContent * Unify task paths * Unify runner function * Fix shadowJarIsCachedCorrectlyWhenOutputFileIsChanged * Remove BasePluginSpecification --- .../shadow/BasePluginSpecification.groovy | 162 -------- .../shadow/caching/AbstractCachingSpec.groovy | 89 ----- .../caching/MinimizationCachingSpec.groovy | 88 ----- .../caching/RelocationCachingSpec.groovy | 68 ---- .../caching/ShadowJarCachingSpec.groovy | 222 ----------- .../caching/TransformCachingSpec.groovy | 355 ------------------ .../gradle/plugins/shadow/BasePluginTest.kt | 58 ++- ...CacheSpec.kt => ConfigurationCacheTest.kt} | 25 +- .../gradle/plugins/shadow/FilteringTest.kt | 4 +- .../gradle/plugins/shadow/RelocationTest.kt | 3 +- .../gradle/plugins/shadow/ShadowPluginTest.kt | 15 +- .../plugins/shadow/caching/BaseCachingTest.kt | 83 ++++ .../shadow/caching/MinimizationCachingTest.kt | 67 ++++ .../shadow/caching/RelocationCachingTest.kt | 63 ++++ .../shadow/caching/ShadowJarCachingTest.kt | 180 +++++++++ .../shadow/caching/TransformCachingTest.kt | 192 ++++++++++ .../transformers/BaseTransformerTest.kt | 28 -- .../gradle/plugins/shadow/util/JarPath.kt | 8 +- 18 files changed, 659 insertions(+), 1051 deletions(-) delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy delete mode 100644 src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy rename src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{ConfigurationCacheSpec.kt => ConfigurationCacheTest.kt} (85%) create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy deleted file mode 100644 index 16efb2799..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/BasePluginSpecification.groovy +++ /dev/null @@ -1,162 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository -import org.apache.commons.lang3.StringUtils -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.GradleRunner -import spock.lang.Shared -import spock.lang.Specification -import spock.lang.TempDir - -import java.nio.file.Files -import java.nio.file.Path -import java.nio.file.Paths -import java.util.jar.JarFile - -abstract class BasePluginSpecification extends Specification { - - @TempDir - Path root - - @Shared - static AppendableMavenRepository repo - - def setupSpec() { - repo = new AppendableMavenRepository( - Files.createTempDirectory(null).resolve('local-maven-repo'), - runner, - ) - repo.module('junit', 'junit', '3.8.2') { module -> - module.useJar(Paths.get(this.class.classLoader.getResource('junit-3.8.2.jar').toURI())) - }.publish() - } - - def setup() { - projectScriptFile << getDefaultProjectBuildScript('java', true, true) - settingsScriptFile << getDefaultSettingsBuildScript() - } - - def cleanup() { - println projectScriptFile.text - } - - def cleanupSpec() { - // TODO: Delete repo recursively. - } - - String getDefaultProjectBuildScript( - String javaPlugin = 'java', - boolean withGroup = false, - boolean withVersion = false - ) { - def groupInfo = withGroup ? "group = 'shadow'" : "" - def versionInfo = withVersion ? "version = '1.0'" : "" - - return """ - plugins { - id '${javaPlugin}' - id 'com.gradleup.shadow' - } - - $groupInfo - $versionInfo - """.stripIndent().trim() + System.lineSeparator() - } - - String getDefaultSettingsBuildScript(boolean withRootProject = true) { - def rootProjectInfo = withRootProject ? "rootProject.name = 'shadow'" : "" - return """ - dependencyResolutionManagement { - repositories { - maven { url = "${repo.root.toUri()}" } - mavenCentral() - } - } - - $rootProjectInfo - """.stripIndent().trim() + System.lineSeparator() - } - - static def shadowJar = "tasks.named('shadowJar', ${ShadowJar.class.name})".trim() - - GradleRunner getRunner() { - def runner = GradleRunner.create() - .forwardOutput() - .withPluginClasspath() - .withTestKitDir(testKitDir) - if (root != null) { - runner.withProjectDir(root.toFile()) - } - return runner - } - - GradleRunner runner(Collection tasks) { - runner.withArguments(["--warning-mode=fail", "--configuration-cache", "--stacktrace"] + tasks.toList()) - } - - BuildResult run(List tasks) { - def result = runner(tasks).build() - result.output.eachLine { output -> - assert !( - output.contains("has been deprecated and is scheduled to be removed in Gradle") || - output.contains("has been deprecated. This is scheduled to be removed in Gradle") - ) - } - return result - } - - File getProjectScriptFile() { - file('build.gradle') - } - - File getSettingsScriptFile() { - file('settings.gradle') - } - - File file(String path) { - File f = root.resolve(path).toFile() - String extension = StringUtils.substringAfterLast(path, '.') - - // Binary files should be asserted to exist, text files should be created. - if (extension == "jar" || extension == "zip") { - return f - } - - if (!f.exists()) { - f.parentFile.mkdirs() - if (!f.createNewFile()) { - throw new IOException("a file with the name \'" + f.name + "\' already exists in the test folder.") - } - } - return f - } - - void assertContains(File f, List paths) { - JarFile jar = new JarFile(f) - paths.each { path -> - assert jar.getJarEntry(path), "${f.path} does not contain [$path]" - } - jar.close() - } - - void assertDoesNotContain(File f, List paths) { - JarFile jar = new JarFile(f) - paths.each { path -> - assert !jar.getJarEntry(path), "${f.path} contains [$path]" - } - jar.close() - } - - File getOutputShadowJar() { - file('build/libs/shadow-1.0-all.jar') - } - - private static File getTestKitDir() { - def gradleUserHome = System.getenv("GRADLE_USER_HOME") - if (!gradleUserHome) { - gradleUserHome = new File(System.getProperty("user.home"), ".gradle").absolutePath - } - return new File(gradleUserHome, "testkit") - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy deleted file mode 100644 index d227f8e79..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/AbstractCachingSpec.groovy +++ /dev/null @@ -1,89 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import com.github.jengelman.gradle.plugins.shadow.BasePluginSpecification -import org.apache.commons.io.FileUtils -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.TaskOutcome -import spock.lang.TempDir - -import java.nio.file.Path - -import static org.gradle.testkit.runner.TaskOutcome.FROM_CACHE -import static org.gradle.testkit.runner.TaskOutcome.SUCCESS - -abstract class AbstractCachingSpec extends BasePluginSpecification { - @TempDir - Path alternateDir - - @Override - def setup() { - // Use a test-specific build cache directory. This ensures that we'll only use cached outputs generated during this - // test and we won't accidentally use cached outputs from a different test or a different build. - settingsScriptFile << """ - buildCache { - local { - directory = new File(rootDir, 'build-cache') - } - } - """ - } - - void changeConfigurationTo(String content) { - projectScriptFile.text = getDefaultProjectBuildScript('java', true, true) - projectScriptFile << content - } - - BuildResult runWithCacheEnabled(String... arguments) { - List cacheArguments = ['--build-cache'] - cacheArguments.addAll(arguments) - return run(cacheArguments) - } - - BuildResult runInAlternateDirWithCacheEnabled(String... arguments) { - List cacheArguments = ['--build-cache'] - cacheArguments.addAll(arguments) - // TODO: Use PluginSpecification.run here to reuse flags, but cache tests failed for now, need to investigate. - return runner.withProjectDir(alternateDir.toFile()).withArguments(cacheArguments).build() - } - - void assertShadowJarHasResult(TaskOutcome expectedOutcome) { - def result = runWithCacheEnabled(shadowJarTask) - assert result.task(shadowJarTask).outcome == expectedOutcome - } - - void assertShadowJarHasResultInAlternateDir(TaskOutcome expectedOutcome) { - def result = runInAlternateDirWithCacheEnabled(shadowJarTask) - assert result.task(shadowJarTask).outcome == expectedOutcome - } - - void copyToAlternateDir() { - FileUtils.deleteDirectory(alternateDir.toFile()) - FileUtils.forceMkdir(alternateDir.toFile()) - FileUtils.copyDirectory(root.toFile(), alternateDir.toFile()) - } - - void assertShadowJarIsCachedAndRelocatable() { - deleteOutputs() - copyToAlternateDir() - // check that shadowJar pulls from cache in the original directory - assertShadowJarHasResult(FROM_CACHE) - // check that shadowJar pulls from cache in a different directory - assertShadowJarHasResultInAlternateDir(FROM_CACHE) - } - - void assertShadowJarExecutes() { - deleteOutputs() - // task was executed and not pulled from cache - assertShadowJarHasResult(SUCCESS) - } - - void deleteOutputs() { - if (outputShadowJar.exists()) { - assert outputShadowJar.delete() - } - } - - String getShadowJarTask() { - return ":shadowJar" - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy deleted file mode 100644 index 5c55d58a8..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingSpec.groovy +++ /dev/null @@ -1,88 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -class MinimizationCachingSpec extends AbstractCachingSpec { - - @Override - String getShadowJarTask() { - return ":server:shadowJar" - } - - @Override - File getOutputShadowJar() { - return file('server/build/libs/server-all.jar') - } - - /** - * Ensure that we get a cache miss when minimization is added and that caching works with minimization - */ - def 'shadowJar is cached correctly when minimization is added'() { - given: - file('settings.gradle') << """ - include 'client', 'server' - """.stripIndent() - - file('client/src/main/java/client/Client.java') << """ - package client; - public class Client {} - """.stripIndent() - - file('client/build.gradle') << """ - apply plugin: 'java' - - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('server/src/main/java/server/Server.java') << """ - package server; - public class Server {} - """.stripIndent() - - file('server/build.gradle') << """ - $defaultProjectBuildScript - - dependencies { implementation project(':client') } - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'junit/framework/Test.class', - 'client/Client.class' - ]) - - when: - file('server/build.gradle') << """ - $shadowJar { - minimize { - exclude(dependency('junit:junit:.*')) - } - } - - dependencies { implementation project(':client') } - """.stripIndent() - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'junit/framework/Test.class' - ]) - assertDoesNotContain(outputShadowJar, ['client/Client.class']) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'junit/framework/Test.class' - ]) - assertDoesNotContain(outputShadowJar, ['client/Client.class']) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy deleted file mode 100644 index 8fbc18a15..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingSpec.groovy +++ /dev/null @@ -1,68 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -class RelocationCachingSpec extends AbstractCachingSpec { - /** - * Ensure that we get a cache miss when relocation changes and that caching works with relocation - */ - def 'shadowJar is cached correctly when relocation is added'() { - given: - projectScriptFile << """ - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('src/main/java/server/Server.java') << """ - package server; - - import junit.framework.Test; - - public class Server {} - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'junit/framework/Test.class' - ]) - - when: - changeConfigurationTo """ - dependencies { implementation 'junit:junit:3.8.2' } - - $shadowJar { - relocate 'junit.framework', 'foo.junit.framework' - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/junit/framework/Test.class' - ]) - - and: - assertDoesNotContain(outputShadowJar, [ - 'junit/framework/Test.class' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/junit/framework/Test.class' - ]) - - and: - assertDoesNotContain(outputShadowJar, [ - 'junit/framework/Test.class' - ]) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy deleted file mode 100644 index 4c786054c..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingSpec.groovy +++ /dev/null @@ -1,222 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -class ShadowJarCachingSpec extends AbstractCachingSpec { - - /** - * Ensure that a basic usage reuses an output from cache and then gets a cache miss when the content changes. - */ - def "shadowJar is cached correctly when copying"() { - given: - URL artifact = this.class.classLoader.getResource('test-artifact-1.0-SNAPSHOT.jar') - URL project = this.class.classLoader.getResource('test-project-1.0-SNAPSHOT.jar') - - projectScriptFile << """ - $shadowJar { - from('${artifact.path}') - from('${project.path}') - } - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - assert outputShadowJar.exists() - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - assert outputShadowJar.exists() - - when: - changeConfigurationTo """ - $shadowJar { - from('${artifact.path}') - } - """ - assertShadowJarExecutes() - - then: - assert outputShadowJar.exists() - } - - /** - * Ensure that an output is reused from the cache if only the output file name is changed. - */ - def "shadowJar is cached correctly when output file is changed"() { - given: - URL artifact = this.class.classLoader.getResource('test-artifact-1.0-SNAPSHOT.jar') - URL project = this.class.classLoader.getResource('test-project-1.0-SNAPSHOT.jar') - - projectScriptFile << """ - $shadowJar { - from('${artifact.path}') - from('${project.path}') - } - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - assert outputShadowJar.exists() - - when: - changeConfigurationTo """ - $shadowJar { - archiveBaseName = "foo" - from('${artifact.path}') - from('${project.path}') - } - """ - assertShadowJarIsCachedAndRelocatable() - - then: - assert !outputShadowJar.exists() - assert file("build/libs/foo-1.0-all.jar").exists() - } - - /** - * Ensure that we get a cache miss when includes/excludes change and that caching works when includes/excludes are present - */ - def 'shadowJar is cached correctly when using includes/excludes'() { - given: - projectScriptFile << """ - dependencies { implementation 'junit:junit:3.8.2' } - - $shadowJar { - exclude 'junit/*' - } - """.stripIndent() - - file('src/main/java/server/Server.java') << """ - package server; - - import junit.framework.Test; - - public class Server {} - """.stripIndent() - - file('src/main/java/server/Util.java') << """ - package server; - - import junit.framework.Test; - - public class Util {} - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'server/Util.class' - ]) - - when: - changeConfigurationTo """ - dependencies { implementation 'junit:junit:3.8.2' } - - $shadowJar { - include 'server/*' - exclude '*/Util.*' - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - and: - assertDoesNotContain(outputShadowJar, [ - 'server/Util.class', - 'junit/framework/Test.class' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - and: - assertDoesNotContain(outputShadowJar, [ - 'server/Util.class', - 'junit/framework/Test.class' - ]) - } - - /** - * Ensure that we get a cache miss when dependency includes/excludes are added and caching works when dependency includes/excludes are present - */ - def 'shadowJar is cached correctly when using dependency includes/excludes'() { - given: - projectScriptFile << """ - dependencies { implementation 'junit:junit:3.8.2' } - """.stripIndent() - - file('src/main/java/server/Server.java') << """ - package server; - - import junit.framework.Test; - - public class Server {} - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'junit/framework/Test.class' - ]) - - when: - changeConfigurationTo """ - dependencies { implementation 'junit:junit:3.8.2' } - - $shadowJar { - dependencies { - exclude(dependency('junit:junit')) - } - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - and: - assertDoesNotContain(outputShadowJar, [ - 'junit/framework/Test.class' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - and: - assertDoesNotContain(outputShadowJar, [ - 'junit/framework/Test.class' - ]) - } -} diff --git a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy b/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy deleted file mode 100644 index e48996e52..000000000 --- a/src/funcTest/groovy/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingSpec.groovy +++ /dev/null @@ -1,355 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer -import spock.lang.Unroll - -class TransformCachingSpec extends AbstractCachingSpec { - /** - * Ensure that that caching is disabled when transforms are used - */ - def 'shadowJar is not cached when custom transforms are used'() { - given: - file('src/main/java/server/Server.java') << """ - package server; - - public class Server {} - """.stripIndent() - - projectScriptFile << """ - import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer - import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext - import org.apache.tools.zip.ZipOutputStream - import org.gradle.api.file.FileTreeElement - - class CustomTransformer implements Transformer { - @Override - boolean canTransformResource(FileTreeElement element) { - return false - } - - @Override - void transform(TransformerContext context) { - - } - - @Override - boolean hasTransformedResource() { - return false - } - - @Override - void modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps) { - - } - } - - $shadowJar { - notCompatibleWithConfigurationCache('CustomTransformer is not cacheable') - transform(CustomTransformer) - } - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - } - - /** - * Ensure that we get a cache miss when ServiceFileTransformer transforms are added and caching works when ServiceFileTransformer transforms are present - */ - @Unroll - def 'shadowJar is cached correctly when using ServiceFileTransformer'() { - given: - file('src/main/java/server/Server.java') << """ - package server; - - public class Server {} - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - when: - // Add a transform - changeConfigurationTo """ - $shadowJar { - transform(${ServiceFileTransformer.name}) { - path = 'META-INF/foo' - } - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - when: - // Change the transform configuration - changeConfigurationTo """ - $shadowJar { - transform(${ServiceFileTransformer.name}) { - path = 'META-INF/bar' - } - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - } - - /** - * Ensure that we get a cache miss when AppendingTransformer transforms are added and caching works when AppendingTransformer transforms are present - */ - @Unroll - def 'shadowJar is cached correctly when using AppendingTransformer'() { - given: - file('src/main/resources/foo/bar.properties') << "foo=bar" - file('src/main/java/server/Server.java') << """ - package server; - - public class Server {} - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - when: - // Add a transform - changeConfigurationTo """ - $shadowJar { - transform(${AppendingTransformer.name}) { - resource = 'foo/bar.properties' - } - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/bar.properties' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/bar.properties' - ]) - - when: - // Change the transform configuration - assert file('src/main/resources/foo/bar.properties').delete() - file('src/main/resources/foo/baz.properties') << "foo=baz" - changeConfigurationTo """ - $shadowJar { - transform(${AppendingTransformer.name}) { - resource = 'foo/baz.properties' - } - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/baz.properties' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/baz.properties' - ]) - } - - /** - * Ensure that we get a cache miss when XmlAppendingTransformer transforms are added and caching works when XmlAppendingTransformer transforms are present - */ - @Unroll - def 'shadowJar is cached correctly when using XmlAppendingTransformer'() { - given: - file('src/main/resources/foo/bar.xml') << "bar" - file('src/main/java/server/Server.java') << """ - package server; - - public class Server {} - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - when: - // Add a transform - changeConfigurationTo """ - $shadowJar { - transform(${XmlAppendingTransformer.name}) { - resource = 'foo/bar.xml' - } - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/bar.xml' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/bar.xml' - ]) - - when: - // Change the transform configuration - assert file('src/main/resources/foo/bar.xml').delete() - file('src/main/resources/foo/baz.xml') << "baz" - changeConfigurationTo """ - $shadowJar { - transform(${AppendingTransformer.name}) { - resource = 'foo/baz.xml' - } - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/baz.xml' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class', - 'foo/baz.xml' - ]) - } - - /** - * Ensure that we get a cache miss when GroovyExtensionModuleTransformer transforms are added and caching works when GroovyExtensionModuleTransformer transforms are present - */ - @Unroll - def 'shadowJar is cached correctly when using GroovyExtensionModuleTransformer'() { - given: - file('src/main/java/server/Server.java') << """ - package server; - - public class Server {} - """.stripIndent() - - when: - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - when: - // Add a transform - changeConfigurationTo """ - $shadowJar { - transform(${GroovyExtensionModuleTransformer.name}) - } - """ - assertShadowJarExecutes() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - - when: - assertShadowJarIsCachedAndRelocatable() - - then: - outputShadowJar.exists() - assertContains(outputShadowJar, [ - 'server/Server.class' - ]) - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index ca6282ba4..01779d04d 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Compan import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarPath import java.nio.file.Path @@ -35,7 +36,10 @@ abstract class BasePluginTest { @BeforeAll open fun doFirst() { - localRepo = AppendableMavenRepository(createTempDirectory().resolve("local-maven-repo"), getRunner(null)) + localRepo = AppendableMavenRepository( + createTempDirectory().resolve("local-maven-repo"), + runner(projectDir = null), + ) localRepo.module("junit", "junit", "3.8.2") { useJar(testJar) }.module("shadow", "a", "1.0") { @@ -107,8 +111,8 @@ abstract class BasePluginTest { """.trimIndent() + System.lineSeparator() } - open val shadowJarTask = SHADOW_JAR_TASK_NAME - open val runShadowTask = SHADOW_RUN_TASK_NAME + open val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" + open val runShadowTask = ":$SHADOW_RUN_TASK_NAME" val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" val projectScriptPath: Path @@ -117,7 +121,7 @@ abstract class BasePluginTest { val settingsScriptPath: Path get() = path("settings.gradle") - val outputShadowJar: JarPath + open val outputShadowJar: JarPath get() = jarPath("build/libs/shadow-1.0-all.jar") val outputServerShadowJar: JarPath @@ -140,10 +144,6 @@ abstract class BasePluginTest { } } - fun runner(arguments: Iterable): GradleRunner { - return getRunner().withArguments(commonArguments + arguments) - } - inline fun run( vararg tasks: String, runnerBlock: (GradleRunner) -> GradleRunner = { it }, @@ -158,6 +158,24 @@ abstract class BasePluginTest { return runnerBlock(runner(tasks.toList())).buildAndFail().assertNoDeprecationWarnings() } + fun writeMainClass( + withImports: Boolean = false, + ) { + val imports = if (withImports) "import junit.framework.Test;" else "" + + path("src/main/java/shadow/Main.java").writeText( + """ + package shadow; + $imports + public class Main { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } + """.trimIndent(), + ) + } + fun writeClientAndServerModules( serverShadowBlock: String = "", ) { @@ -276,10 +294,14 @@ abstract class BasePluginTest { ) } - private fun getRunner(projectDir: Path? = root) = GradleRunner.create() + fun runner( + arguments: Iterable = emptyList(), + projectDir: Path? = root, + ): GradleRunner = GradleRunner.create() .forwardOutput() .withPluginClasspath() .withTestKitDir(testKitDir.toFile()) + .withArguments(commonArguments + arguments) .apply { if (projectDir != null) { withProjectDir(projectDir.toFile()) @@ -296,6 +318,10 @@ abstract class BasePluginTest { } val testJar: Path = requireNotNull(this::class.java.classLoader.getResource("junit-3.8.2.jar")).toURI().toPath() + val artifactJar: Path = + requireNotNull(this::class.java.classLoader.getResource("test-artifact-1.0-SNAPSHOT.jar")).toURI().toPath() + val projectJar: Path = + requireNotNull(this::class.java.classLoader.getResource("test-project-1.0-SNAPSHOT.jar")).toURI().toPath() val shadowJar: String = """ tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name}) @@ -317,6 +343,20 @@ abstract class BasePluginTest { return paths.joinToString(System.lineSeparator()) { "from('${it.toUri().toURL().path}')" } } + inline fun transform( + shadowJarBlock: String = "", + transformerBlock: String = "", + ): String { + return """ + $shadowJar { + $shadowJarBlock + transform(${T::class.java.name}) { + $transformerBlock + } + } + """.trimIndent() + } + fun BuildResult.assertNoDeprecationWarnings() = apply { output.lines().forEach { assert(!containsDeprecationWarning(it)) diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheTest.kt similarity index 85% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheTest.kt index 168e4e6a1..29539c00f 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheSpec.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheTest.kt @@ -4,6 +4,7 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEqualTo import assertk.assertions.isNotNull +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import kotlin.io.path.appendText @@ -13,7 +14,7 @@ import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -class ConfigurationCacheSpec : BasePluginTest() { +class ConfigurationCacheTest : BasePluginTest() { @BeforeEach override fun setup() { super.setup() @@ -29,16 +30,7 @@ class ConfigurationCacheSpec : BasePluginTest() { @Test fun supportsConfigurationCache() { - path("src/main/java/myapp/Main.java").writeText( - """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.trimIndent(), - ) + writeMainClass() projectScriptPath.appendText( """ @@ -93,9 +85,9 @@ class ConfigurationCacheSpec : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(serverShadowJarTask) outputServerShadowJar.deleteExisting() - val result = run(shadowJarTask) + val result = run(serverShadowJarTask) assertThat(outputServerShadowJar).containsEntries( "server/Server.class", @@ -133,10 +125,11 @@ class ConfigurationCacheSpec : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) - val result = run(shadowJarTask) + val libShadowJarTask = ":lib:$SHADOW_JAR_TASK_NAME" + run(libShadowJarTask) + val result = run(libShadowJarTask) - assertThat(result.task(":lib:shadowJar")).isNotNull() + assertThat(result.task(libShadowJarTask)).isNotNull() .transform { it.outcome }.isEqualTo(TaskOutcome.UP_TO_DATE) result.assertCcReused() } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index d4c5621a9..a79f2b32c 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -106,7 +106,7 @@ class FilteringTest : BasePluginTest() { projectScriptPath.writeText(replaced) val result = run(shadowJarTask) - assertThat(result.task(":shadowJar")).isNotNull() + assertThat(result.task(shadowJarTask)).isNotNull() .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) assertThat(outputShadowJar).containsEntries( "a.properties", @@ -133,7 +133,7 @@ class FilteringTest : BasePluginTest() { val result = run(shadowJarTask) - assertThat(result.task(":shadowJar")).isNotNull() + assertThat(result.task(shadowJarTask)).isNotNull() .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) assertThat(outputShadowJar).containsEntries( "a2.properties", diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index e79716bd6..8571c350d 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -4,6 +4,7 @@ import assertk.assertFailure import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries @@ -269,7 +270,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(":app:$shadowJarTask") + run(":app:$SHADOW_JAR_TASK_NAME") val appOutput = jarPath("app/build/libs/app-all.jar") assertThat(appOutput).containsEntries( diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt index d64ea99fa..bb1343508 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -6,6 +6,7 @@ import assertk.assertions.isEqualTo import assertk.assertions.isNotNull import assertk.assertions.isNull import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.Issue @@ -14,7 +15,6 @@ import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import com.github.jengelman.gradle.plugins.shadow.util.isRegular import kotlin.io.path.appendText import kotlin.io.path.readText -import kotlin.io.path.toPath import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin import org.gradle.testfixtures.ProjectBuilder @@ -38,10 +38,10 @@ class ShadowPluginTest : BasePluginTest() { assertThat(project.plugins.hasPlugin(ShadowPlugin::class.java)).isTrue() assertThat(project.plugins.hasPlugin(LegacyShadowPlugin::class.java)).isTrue() - assertThat(project.tasks.findByName(shadowJarTask)).isNull() + assertThat(project.tasks.findByName(SHADOW_JAR_TASK_NAME)).isNull() project.plugins.apply(JavaPlugin::class.java) - val shadowTask = project.tasks.getByName(shadowJarTask) as ShadowJar + val shadowTask = project.tasks.getByName(SHADOW_JAR_TASK_NAME) as ShadowJar val shadowConfig = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) assertThat(shadowTask.archiveBaseName.get()).isEqualTo(projectName) @@ -83,11 +83,6 @@ class ShadowPluginTest : BasePluginTest() { @Test fun shadowCopy() { - val artifactJar = requireNotNull(this::class.java.classLoader.getResource("test-artifact-1.0-SNAPSHOT.jar")) - .toURI().toPath() - val projectJar = requireNotNull(this::class.java.classLoader.getResource("test-project-1.0-SNAPSHOT.jar")) - .toURI().toPath() - projectScriptPath.appendText( """ $shadowJar { @@ -287,7 +282,7 @@ class ShadowPluginTest : BasePluginTest() { fun useMinimizeWithDependenciesWithApiScope() { writeApiLibAndImplModules() - run(":impl:$shadowJarTask") + run(":impl:$SHADOW_JAR_TASK_NAME") val implOutput = jarPath("impl/build/libs/impl-all.jar") assertThat(implOutput).containsEntries( @@ -320,7 +315,7 @@ class ShadowPluginTest : BasePluginTest() { """.trimIndent(), ) - run(":impl:$shadowJarTask") + run(":impl:$SHADOW_JAR_TASK_NAME") val implOutput = jarPath("impl/build/libs/impl-all.jar") assertThat(implOutput).containsEntries( diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt new file mode 100644 index 000000000..b12cf69fa --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt @@ -0,0 +1,83 @@ +package com.github.jengelman.gradle.plugins.shadow.caching + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull +import com.github.jengelman.gradle.plugins.shadow.BasePluginTest +import java.nio.file.Path +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.appendText +import kotlin.io.path.copyToRecursively +import kotlin.io.path.deleteRecursively +import kotlin.io.path.listDirectoryEntries +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.io.TempDir + +abstract class BaseCachingTest : BasePluginTest() { + + @TempDir + lateinit var alternateDir: Path + + @BeforeEach + override fun setup() { + super.setup() + // Use a test-specific build cache directory. This ensures that we'll only use cached outputs generated during + // this test, and we won't accidentally use cached outputs from a different test or a different build. + settingsScriptPath.appendText( + """ + buildCache { + local { + directory = file('build-cache') + } + } + """.trimIndent() + System.lineSeparator(), + ) + } + + @OptIn(ExperimentalPathApi::class) + fun assertShadowJarIsCachedAndRelocatable( + firstOutcome: TaskOutcome = FROM_CACHE, + secondOutcome: TaskOutcome = UP_TO_DATE, + ) { + try { + outputShadowJar.deleteExisting() + } catch (ignored: IllegalStateException) { + // ignore if the file does not exist + } + alternateDir.deleteRecursively() + root.copyToRecursively(alternateDir, followLinks = false, overwrite = false) + // check that shadowJar pulls from cache in the original directory + assertShadowJarHasResult(firstOutcome) + // check that shadowJar pulls from cache in a different directory + assertShadowJarHasResult(secondOutcome) { + if (alternateDir.listDirectoryEntries().isEmpty()) { + error("Directory was not copied to alternate directory") + } + it.withProjectDir(alternateDir.toFile()) + } + } + + fun assertShadowJarExecutes() { + try { + outputShadowJar.deleteExisting() + } catch (ignored: IllegalStateException) { + // ignore if the file does not exist + } + // task was executed and not pulled from cache + assertShadowJarHasResult(SUCCESS) + } + + private fun assertShadowJarHasResult( + expectedOutcome: TaskOutcome, + runnerBlock: (GradleRunner) -> GradleRunner = { it }, + ) { + val result = run("--build-cache", shadowJarTask, runnerBlock = runnerBlock) + assertThat(result.task(shadowJarTask)).isNotNull() + .transform { it.outcome }.isEqualTo(expectedOutcome) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt new file mode 100644 index 000000000..e1558090c --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt @@ -0,0 +1,67 @@ +package com.github.jengelman.gradle.plugins.shadow.caching + +import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.util.JarPath +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.useAll +import kotlin.io.path.appendText +import kotlin.io.path.writeText +import org.junit.jupiter.api.Test + +class MinimizationCachingTest : BaseCachingTest() { + override val shadowJarTask: String = serverShadowJarTask + override val outputShadowJar: JarPath get() = outputServerShadowJar + + @Test + fun shadowJarIsCachedCorrectlyWhenMinimizationIsAdded() { + writeClientAndServerModules() + path("server/src/main/java/server/Server.java").writeText( + """ + package server; + public class Server {} + """.trimIndent(), + ) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries( + "server/Server.class", + "junit/framework/Test.class", + "client/Client.class", + ) + } + + path("server/build.gradle").appendText( + """ + $shadowJar { + minimize { + exclude(dependency('junit:junit:.*')) + } + } + """.trimIndent(), + ) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries( + "server/Server.class", + "junit/framework/Test.class", + ) + doesNotContainEntries( + "client/Client.class", + ) + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries( + "server/Server.class", + "junit/framework/Test.class", + ) + doesNotContainEntries( + "client/Client.class", + ) + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt new file mode 100644 index 000000000..a62b217d5 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -0,0 +1,63 @@ +package com.github.jengelman.gradle.plugins.shadow.caching + +import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.useAll +import kotlin.io.path.appendText +import org.junit.jupiter.api.Test + +class RelocationCachingTest : BaseCachingTest() { + /** + * Ensure that we get a cache miss when relocation changes and that caching works with relocation + */ + @Test + fun shadowJarIsCachedCorrectlyWhenRelocationIsAdded() { + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent() + System.lineSeparator(), + ) + writeMainClass(withImports = true) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + "junit/framework/Test.class", + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + relocate 'junit.framework', 'foo.junit.framework' + } + """.trimIndent(), + ) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + "foo/junit/framework/Test.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + "foo/junit/framework/Test.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt new file mode 100644 index 000000000..3c99cc192 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -0,0 +1,180 @@ +package com.github.jengelman.gradle.plugins.shadow.caching + +import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.isRegular +import com.github.jengelman.gradle.plugins.shadow.util.useAll +import kotlin.io.path.appendText +import kotlin.io.path.readText +import kotlin.io.path.writeText +import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE +import org.junit.jupiter.api.Test + +class ShadowJarCachingTest : BaseCachingTest() { + /** + * Ensure that a basic usage reuses an output from cache and then gets a cache miss when the content changes. + */ + @Test + fun shadowJarIsCachedCorrectlyWhenCopying() { + projectScriptPath.appendText( + """ + $shadowJar { + ${fromJar(artifactJar, projectJar)} + } + """.trimIndent(), + ) + + assertShadowJarExecutes() + assertShadowJarIsCachedAndRelocatable() + + val replaced = projectScriptPath.readText().lines().filter { + it != fromJar(projectJar) + }.joinToString(System.lineSeparator()) + projectScriptPath.writeText(replaced) + assertShadowJarExecutes() + } + + @Test + fun shadowJarIsCachedCorrectlyWhenOutputFileIsChanged() { + projectScriptPath.appendText( + """ + $shadowJar { + ${fromJar(artifactJar, projectJar)} + } + """.trimIndent() + System.lineSeparator(), + ) + + assertShadowJarExecutes() + + projectScriptPath.appendText( + """ + $shadowJar { + archiveBaseName = "foo" + } + """.trimIndent(), + ) + // TODO: need to investigate why secondOutcome is FROM_CACHE instead of UP_TO_DATE. + assertShadowJarIsCachedAndRelocatable(secondOutcome = FROM_CACHE) + assertThat(jarPath("build/libs/foo-1.0-all.jar")).isRegular() + } + + @Test + fun shadowJarIsCachedCorrectlyWhenUsingIncludesExcludes() { + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + exclude 'junit/*' + } + """.trimIndent(), + ) + + path("src/main/java/server/Server.java").writeText( + """ + package server; + import junit.framework.Test; + public class Server {} + """.trimIndent(), + ) + path("src/main/java/server/Util.java").writeText( + """ + package server; + import junit.framework.Test; + public class Util {} + """.trimIndent(), + ) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries( + "server/Server.class", + "server/Util.class", + ) + } + + val replaced = projectScriptPath.readText().lines().dropLast(3).joinToString(System.lineSeparator()) + projectScriptPath.writeText( + """ + $replaced + $shadowJar { + include 'server/*' + exclude '*/Util.*' + } + """.trimIndent(), + ) + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries( + "server/Server.class", + ) + doesNotContainEntries( + "server/Util.class", + "junit/framework/Test.class", + ) + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries( + "server/Server.class", + ) + doesNotContainEntries( + "server/Util.class", + "junit/framework/Test.class", + ) + } + } + + @Test + fun shadowJarIsCachedCorrectlyWhenUsingDependencyIncludesExcludes() { + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent() + System.lineSeparator(), + ) + + writeMainClass(withImports = true) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + "junit/framework/Test.class", + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + dependencies { + exclude(dependency('junit:junit')) + } + } + """.trimIndent(), + ) + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt new file mode 100644 index 000000000..c749a6582 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt @@ -0,0 +1,192 @@ +package com.github.jengelman.gradle.plugins.shadow.caching + +import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.NoOpTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.useAll +import kotlin.io.path.appendText +import kotlin.io.path.readText +import kotlin.io.path.writeText +import org.junit.jupiter.api.Test + +class TransformCachingTest : BaseCachingTest() { + @Test + fun shadowJarIsNotCachedWhenCustomTransformsAreUsed() { + writeMainClass() + projectScriptPath.appendText( + """ + $shadowJar { + // Use NoOpTransformer to mock a custom transformer here. + transform(${NoOpTransformer::class.java.name}.INSTANCE) + } + """.trimIndent(), + ) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + } + + @Test + fun shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer() { + writeMainClass() + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + + projectScriptPath.appendText( + transform( + transformerBlock = """ + path = 'META-INF/foo' + """.trimIndent(), + ), + ) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + + val replaced = projectScriptPath.readText().replace("META-INF/foo", "META-INF/bar") + projectScriptPath.writeText(replaced) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + } + + @Test + fun shadowJarIsCachedCorrectlyWhenUsingAppendingTransformer() { + path("src/main/resources/foo/bar.properties").writeText("foo=bar") + writeMainClass() + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + + projectScriptPath.appendText( + transform( + transformerBlock = """ + resource = 'foo/bar.properties' + """.trimIndent(), + ), + ) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/bar.properties") + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/bar.properties") + } + + path("src/main/resources/foo/bar.properties").toFile().delete() + path("src/main/resources/foo/baz.properties").writeText("foo=baz") + val replaced = projectScriptPath.readText().replace("foo/bar.properties", "foo/baz.properties") + projectScriptPath.writeText(replaced) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/baz.properties") + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/baz.properties") + } + } + + @Test + fun shadowJarIsCachedCorrectlyWhenUsingXmlAppendingTransformer() { + path("src/main/resources/foo/bar.xml").writeText("bar") + writeMainClass() + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + + projectScriptPath.appendText( + transform( + transformerBlock = """ + resource = 'foo/bar.xml' + """.trimIndent(), + ), + ) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/bar.xml") + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/bar.xml") + } + + path("src/main/resources/foo/bar.xml").toFile().delete() + path("src/main/resources/foo/baz.xml").writeText("baz") + val replaced = projectScriptPath.readText().replace("foo/bar.xml", "foo/baz.xml") + projectScriptPath.writeText(replaced) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/baz.xml") + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/baz.xml") + } + } + + @Test + fun shadowJarIsCachedCorrectlyWhenUsingGroovyExtensionModuleTransformer() { + writeMainClass() + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + + projectScriptPath.appendText( + transform(), + ) + + assertShadowJarExecutes() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + + assertShadowJarIsCachedAndRelocatable() + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 7f09688a6..557275a4d 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.BasePluginTest import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder import java.nio.file.Path -import kotlin.io.path.writeText sealed class BaseTransformerTest : BasePluginTest() { @@ -29,19 +28,6 @@ sealed class BaseTransformerTest : BasePluginTest() { return JarBuilder(path(path)).apply(builder).write() } - fun writeMainClass() { - path("src/main/java/shadow/Main.java").writeText( - """ - package shadow; - public class Main { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } - } - """.trimIndent(), - ) - } - protected companion object { const val CONTENT_ONE = "one # NOTE: No newline terminates this line/file" const val CONTENT_TWO = "two # NOTE: No newline terminates this line/file" @@ -52,19 +38,5 @@ sealed class BaseTransformerTest : BasePluginTest() { const val ENTRY_SERVICES_SHADE = "META-INF/services/org.apache.maven.Shade" const val ENTRY_SERVICES_FOO = "META-INF/services/com.acme.Foo" const val ENTRY_FOO_SHADE = "META-INF/foo/org.apache.maven.Shade" - - inline fun transform( - shadowJarBlock: String = "", - transformerBlock: String = "", - ): String { - return """ - $shadowJar { - $shadowJarBlock - transform(${T::class.java.name}) { - $transformerBlock - } - } - """.trimIndent() - } } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index 9f9ab7409..d7f7b3c9f 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.util import assertk.Assert +import assertk.all import assertk.assertions.isNotEmpty import assertk.fail import java.nio.file.Path @@ -24,10 +25,15 @@ class JarPath(val path: Path) : fun getContent(entryName: String): String { val entry = getEntry(entryName) ?: error("Entry not found: $entryName") - return getInputStream(entry).bufferedReader().readText() + return getInputStream(entry).bufferedReader().use { it.readText() } } } +fun Assert.useAll(body: Assert.() -> Unit) = all { + body() + given { it.close() } +} + /** * Common regular assertions for [JarPath]. */ From 710e7b8b6b1ff6a66563fbea4129c87db46a9d1b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:33:49 +0800 Subject: [PATCH 131/941] Update plugin android-lint to v8.8.0 (#1138) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5050e5492..e48c47590 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,6 +38,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin = "org.jetbrains.kotlin.jvm:2.1.0" -android-lint = "com.android.lint:8.7.3" +android-lint = "com.android.lint:8.8.0" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" spotless = "com.diffplug.spotless:7.0.1" From 3d9e3560f896850b97a316c4e670595bb6d4e8e6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 10 Jan 2025 04:11:42 -0500 Subject: [PATCH 132/941] Finish functional tests migration (#1117) * Move funcTest sources back * Reduce extra doc package in intiTest * Seems we don't need an explicit ConfigurationCacheTest anymore * Close things in tests * Simplify task outcome checks * Cleanups * Simplify assertions for JarPath * Remove the workaround for projectRoot * Tweak lint configs * Remove TODO for excludeProjectFromMinimizeShallNotExcludeTransitiveDependenciesThatAreUsedInSubproject * Simplify checks for jarPath * Create jars in temp dir * Fix deprecations * Unify classLoader resource usages * Simplify requireResourceAsText and requireResourceAsStream * Optimize closable using * Fix org.junit.jupiter.api.extension.Extension path * Assert the bat file in integrationWithApplicationPluginAndJavaToolchains * Enable --build-cache for all func tests * Optimize assertions in BaseCachingTest and remove alternateDir logic --- build.gradle.kts | 41 +-- gradle/libs.versions.toml | 10 +- lint-baseline.xml | 8 +- src/funcTest/.editorconfig | 2 - .../gradle/plugins/shadow/ApplicationTest.kt | 48 ++- .../gradle/plugins/shadow/BasePluginTest.kt | 101 +++--- .../gradle/plugins/shadow/FilteringTest.kt | 168 +++++---- .../gradle/plugins/shadow/PublishingTest.kt | 51 +-- .../gradle/plugins/shadow/RelocationTest.kt | 255 ++++++------- .../gradle/plugins/shadow/ShadowPluginTest.kt | 337 ++++++++++-------- .../plugins/shadow/caching/BaseCachingTest.kt | 46 +++ .../shadow/caching/MinimizationCachingTest.kt | 7 +- .../shadow/caching/RelocationCachingTest.kt | 7 +- .../shadow/caching/ShadowJarCachingTest.kt | 25 +- .../shadow/caching/TransformCachingTest.kt | 61 +--- .../transformers/AppendingTransformerTest.kt | 8 +- .../transformers/BaseTransformerTest.kt | 7 +- .../GroovyExtensionModuleTransformerTest.kt | 38 +- .../ServiceFileTransformerTest.kt | 71 ++-- .../shadow/transformers/TransformersTest.kt | 66 ++-- .../shadow/util/AppendableMavenRepository.kt | 4 +- .../shadow/util/GradleModuleMetadata.kt | 0 .../gradle/plugins/shadow/util/Issue.kt | 0 .../gradle/plugins/shadow/util/JarBuilder.kt | 6 +- .../gradle/plugins/shadow/util/JarPath.kt | 25 +- .../org.junit.jupiter.api.extension.Extension | 0 .../plugins/shadow/ConfigurationCacheTest.kt | 140 -------- .../shadow/{doc => }/DocCodeSnippetTest.kt | 10 +- .../plugins/shadow/caching/BaseCachingTest.kt | 83 ----- .../shadow/doc/executor/SnippetExecutor.kt | 10 - .../shadow/doc/fixture/SnippetFixture.kt | 5 - .../executable/CodeSnippetExecutable.kt | 4 +- .../executable/CodeSnippetExtractor.kt | 6 +- .../{doc => }/executor/GroovyBuildExecutor.kt | 4 +- .../shadow/{doc => }/executor/NoopExecutor.kt | 4 +- .../shadow/executor/SnippetExecutor.kt | 10 + .../{doc => }/fixture/GroovyDslFixture.kt | 2 +- .../plugins/shadow/fixture/SnippetFixture.kt | 5 + src/intiTest/resources/junit-3.8.2.jar | Bin 120640 -> 0 bytes .../resources/test-artifact-1.0-SNAPSHOT.jar | Bin 3115 -> 0 bytes .../resources/test-project-1.0-SNAPSHOT.jar | Bin 3906 -> 0 bytes .../plugins/shadow/ShadowApplicationPlugin.kt | 9 +- .../gradle/plugins/shadow/internal/Utils.kt | 13 +- .../gradle/plugins/shadow/tasks/KnowsTask.kt | 2 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 1 + .../transformers/AppendingTransformer.kt | 1 + .../transformers/PropertiesFileTransformer.kt | 4 +- .../transformers/BaseTransformerTest.kt | 8 +- .../ComponentsXmlResourceTransformerTest.kt | 1 + .../Log4j2PluginsCacheFileTransformerTest.kt | 8 +- .../ManifestAppenderTransformerTest.kt | 1 + .../ServiceFileTransformerTest.kt | 4 +- 52 files changed, 813 insertions(+), 914 deletions(-) delete mode 100755 src/funcTest/.editorconfig rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt (71%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt (78%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt (67%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt (90%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt (54%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt (74%) create mode 100644 src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt (90%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt (90%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt (86%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt (76%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt (80%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt (86%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt (75%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt (72%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt (76%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt (96%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt (100%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt (100%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt (87%) rename src/{intiTest => funcTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt (66%) rename src/{intiTest => funcTest}/resources/META-INF/services/org.junit.jupiter.api.extension.Extension (100%) delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheTest.kt rename src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{doc => }/DocCodeSnippetTest.kt (67%) delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt delete mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt rename src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{doc => }/executable/CodeSnippetExecutable.kt (81%) rename src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{doc => }/executable/CodeSnippetExtractor.kt (92%) rename src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{doc => }/executor/GroovyBuildExecutor.kt (92%) rename src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{doc => }/executor/NoopExecutor.kt (61%) create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt rename src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{doc => }/fixture/GroovyDslFixture.kt (90%) create mode 100644 src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt delete mode 100644 src/intiTest/resources/junit-3.8.2.jar delete mode 100644 src/intiTest/resources/test-artifact-1.0-SNAPSHOT.jar delete mode 100644 src/intiTest/resources/test-project-1.0-SNAPSHOT.jar diff --git a/build.gradle.kts b/build.gradle.kts index 4b1a05d86..03f2e1838 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -2,11 +2,10 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion plugins { - alias(libs.plugins.kotlin) + alias(libs.plugins.kotlin.jvm) alias(libs.plugins.android.lint) alias(libs.plugins.jetbrains.bcv) alias(libs.plugins.spotless) - groovy // Required for Spock tests. id("shadow.convention.publish") id("shadow.convention.deploy") } @@ -30,6 +29,8 @@ kotlin { lint { baseline = file("lint-baseline.xml") + ignoreTestSources = true + warningsAsErrors = true } spotless { @@ -54,15 +55,12 @@ val intiTestRuntimeOnly: Configuration by configurations.getting { val funcTest: SourceSet by sourceSets.creating val funcTestImplementation: Configuration by configurations.getting { extendsFrom(configurations.testImplementation.get()) - // TODO: this will be removed after we migrated all functional tests to Kotlin. - extendsFrom(intiTestImplementation) } val funcTestRuntimeOnly: Configuration by configurations.getting { extendsFrom(configurations.testRuntimeOnly.get()) } gradlePlugin { - testSourceSets.add(intiTest) testSourceSets.add(funcTest) } @@ -80,26 +78,14 @@ dependencies { testImplementation(libs.junit.jupiter) testImplementation(libs.assertk) testImplementation(libs.xmlunit) - testImplementation(libs.apache.commonsLang) testRuntimeOnly(libs.junit.platformLauncher) - funcTestImplementation(libs.spock) { - exclude(group = "org.codehaus.groovy") - exclude(group = "org.hamcrest") - } funcTestImplementation(sourceSets.main.get().output) - funcTestImplementation(intiTest.output) - - intiTestImplementation(libs.okio) - intiTestImplementation(libs.apache.maven.modelBuilder) - intiTestImplementation(libs.apache.maven.repositoryMetadata) - // TODO: this will be removed after we migrated all functional tests to Kotlin. - intiTestImplementation(sourceSets.main.get().output) - intiTestImplementation(libs.moshi) - intiTestImplementation(libs.moshi.kotlin) + funcTestImplementation(libs.apache.maven.modelBuilder) + funcTestImplementation(libs.moshi) + funcTestImplementation(libs.moshi.kotlin) lintChecks(libs.androidx.gradlePluginLints) - lintChecks(libs.assertk.lint) } val integrationTest by tasks.registering(Test::class) { @@ -108,10 +94,6 @@ val integrationTest by tasks.registering(Test::class) { testClassesDirs = intiTest.output.classesDirs classpath = intiTest.runtimeClasspath - // TODO: this should be moved into functionalTest after we migrated all functional tests to Kotlin. - // Required to enable `IssueExtension` for all tests. - systemProperty("junit.jupiter.extensions.autodetection.enabled", true) - val docsDir = file("src/docs") // Add src/docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. inputs.dir(docsDir) @@ -123,6 +105,9 @@ val functionalTest by tasks.registering(Test::class) { group = LifecycleBasePlugin.VERIFICATION_GROUP testClassesDirs = funcTest.output.classesDirs classpath = funcTest.runtimeClasspath + + // Required to enable `IssueExtension` for all tests. + systemProperty("junit.jupiter.extensions.autodetection.enabled", true) } tasks.check { @@ -147,14 +132,6 @@ tasks.withType().configureEach { ) } -tasks.whenTaskAdded { - if (name == "lintAnalyzeJvmTest") { - // This task often fails on Windows CI devices. - enabled = !providers.systemProperty("os.name").get().startsWith("Windows") && - !providers.environmentVariable("CI").isPresent - } -} - tasks.clean { val includedBuilds = gradle.includedBuilds dependsOn(includedBuilds.map { it.task(path) }) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e48c47590..478001332 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,21 +1,17 @@ [versions] -maven = "3.9.9" moshi = "1.15.2" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.18.0" -apache-commonsLang = "org.apache.commons:commons-lang3:3.17.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" -apache-maven-modelBuilder = { module = "org.apache.maven:maven-model-builder", version.ref = "maven" } -apache-maven-repositoryMetadata = { module = "org.apache.maven:maven-repository-metadata", version.ref = "maven" } +apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.9" asm = "org.ow2.asm:asm-commons:9.7.1" jdependency = "org.vafer:jdependency:2.11" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" -okio = "com.squareup.okio:okio:3.10.2" moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } @@ -26,18 +22,16 @@ jetbrains-dokka = "org.jetbrains.dokka:dokka-gradle-plugin:2.0.0" node = "com.github.node-gradle:gradle-node-plugin:7.1.0" androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha03" -assertk-lint = "com.jzbrooks:assertk-lint:1.4.0" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" -spock = "org.spockframework:spock-core:2.3-groovy-3.0" junit-bom = "org.junit:junit-bom:5.11.4" junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" } junit-platformLauncher = { module = "org.junit.platform:junit-platform-launcher" } assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] -kotlin = "org.jetbrains.kotlin.jvm:2.1.0" +kotlin-jvm = "org.jetbrains.kotlin.jvm:2.1.0" android-lint = "com.android.lint:8.8.0" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" spotless = "com.diffplug.spotless:7.0.1" diff --git a/lint-baseline.xml b/lint-baseline.xml index 6f209b0af..494ab0b7c 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -107,7 +107,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +118,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,7 +129,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +140,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/funcTest/.editorconfig b/src/funcTest/.editorconfig deleted file mode 100755 index 3c75e2d26..000000000 --- a/src/funcTest/.editorconfig +++ /dev/null @@ -1,2 +0,0 @@ -[*.{groovy,java}] -indent_size = 4 diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt similarity index 71% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt index 2d05cec1a..8f3ea3e97 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt @@ -1,12 +1,14 @@ package com.github.jengelman.gradle.plugins.shadow +import assertk.all import assertk.assertThat import assertk.assertions.contains import assertk.assertions.containsAtLeast import assertk.assertions.exists import assertk.assertions.isEqualTo -import assertk.assertions.isNotEmpty import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr +import com.github.jengelman.gradle.plugins.shadow.util.isRegular import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText @@ -36,23 +38,33 @@ class ApplicationTest : BasePluginTest() { val result = run(runShadowTask) - assertThat(result.output).contains("Running application with JDK 17") - assertThat(result.output).contains("TestApp: Hello World! (foo)") - - val installedJar = jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar") - assertThat(installedJar).containsEntries( - "a.properties", - "a2.properties", - "myapp/Main.class", + assertThat(result.output).contains( + "Running application with JDK 17", + "TestApp: Hello World! (foo)", ) - assertThat(installedJar.manifest.mainAttributes.getValue("Main-Class")) - .isEqualTo("myapp.Main") - path("build/install/myapp-shadow/bin/myapp").let { startScript -> - assertThat(startScript).exists() - assertThat(startScript.readText()).contains("CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar") - assertThat(startScript.readText()).contains("-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"") - assertThat(startScript.readText()).contains("exec \"\$JAVACMD\" \"\$@\"") + assertThat(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")).useAll { + containsEntries( + "a.properties", + "a2.properties", + "myapp/Main.class", + ) + getMainAttr("Main-Class").isEqualTo("myapp.Main") + } + + assertThat(path("build/install/myapp-shadow/bin/myapp")).all { + exists() + transform { it.readText() }.contains( + "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", + "-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"", + "exec \"\$JAVACMD\" \"\$@\"", + ) + } + assertThat(path("build/install/myapp-shadow/bin/myapp.bat")).all { + exists() + transform { it.readText() }.contains( + "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", + ) } } @@ -71,7 +83,7 @@ class ApplicationTest : BasePluginTest() { val zip = path("build/distributions/myapp-shadow-1.0.zip") assertThat(zip).exists() - val entries = ZipFile(zip.toFile()).entries.toList().map { it.name } + val entries = ZipFile(zip.toFile()).use { it.entries }.toList().map { it.name } assertThat(entries).containsAtLeast( "myapp-shadow-1.0/lib/myapp-1.0-all.jar", "myapp-shadow-1.0/lib/a-1.0.jar", @@ -84,7 +96,7 @@ class ApplicationTest : BasePluginTest() { run(ShadowApplicationPlugin.SHADOW_INSTALL_TASK_NAME) - assertThat(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar").entries().toList()).isNotEmpty() + assertThat(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")).isRegular() } private fun prepare( diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt similarity index 78% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt index 01779d04d..2564ddfa3 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt @@ -1,5 +1,11 @@ package com.github.jengelman.gradle.plugins.shadow +import assertk.Assert +import assertk.all +import assertk.assertThat +import assertk.assertions.doesNotContain +import assertk.assertions.isEqualTo +import assertk.assertions.isNotNull import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec @@ -7,6 +13,8 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarPath +import java.io.Closeable +import java.nio.file.NoSuchFileException import java.nio.file.Path import java.util.Properties import kotlin.io.path.ExperimentalPathApi @@ -18,20 +26,22 @@ import kotlin.io.path.createFile import kotlin.io.path.createTempDirectory import kotlin.io.path.deleteRecursively import kotlin.io.path.exists -import kotlin.io.path.isRegularFile import kotlin.io.path.readText import kotlin.io.path.toPath import kotlin.io.path.writeText import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.io.TempDir @TestInstance(TestInstance.Lifecycle.PER_CLASS) abstract class BasePluginTest { - lateinit var root: Path + @TempDir + lateinit var projectRoot: Path lateinit var localRepo: AppendableMavenRepository @BeforeAll @@ -56,20 +66,12 @@ abstract class BasePluginTest { @BeforeEach open fun setup() { - root = createTempDirectory() - projectScriptPath.writeText(getDefaultProjectBuildScript(withGroup = true, withVersion = true)) settingsScriptPath.writeText(getDefaultSettingsBuildScript()) } - @ExperimentalPathApi @AfterEach fun cleanup() { - runCatching { - // TODO: workaround for https://github.com/junit-team/junit5/issues/2811. - root.deleteRecursively() - } - println(projectScriptPath.readText()) } @@ -78,6 +80,15 @@ abstract class BasePluginTest { localRepo.root.deleteRecursively() } + open val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" + open val runShadowTask = ":$SHADOW_RUN_TASK_NAME" + val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" + + val projectScriptPath: Path get() = path("build.gradle") + val settingsScriptPath: Path get() = path("settings.gradle") + open val outputShadowJar: JarPath get() = jarPath("build/libs/shadow-1.0-all.jar") + val outputServerShadowJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") + fun getDefaultProjectBuildScript( javaPlugin: String = "java", withGroup: Boolean = false, @@ -97,6 +108,10 @@ abstract class BasePluginTest { fun getDefaultSettingsBuildScript( startBlock: String = "", + // Use a test-specific build cache directory. This ensures that we'll only use cached outputs generated during + // this test, and we won't accidentally use cached outputs from a different test or a different build. + // https://docs.gradle.org/current/userguide/build_cache.html#sec:build_cache_configure_local + buildCacheBlock: String = "local { directory = file('build-cache') }", endBlock: String = "rootProject.name = 'shadow'", ): String { return """ @@ -107,36 +122,19 @@ abstract class BasePluginTest { mavenCentral() } } + buildCache { + $buildCacheBlock + } $endBlock """.trimIndent() + System.lineSeparator() } - open val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" - open val runShadowTask = ":$SHADOW_RUN_TASK_NAME" - val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" - - val projectScriptPath: Path - get() = path("build.gradle") - - val settingsScriptPath: Path - get() = path("settings.gradle") - - open val outputShadowJar: JarPath - get() = jarPath("build/libs/shadow-1.0-all.jar") - - val outputServerShadowJar: JarPath - get() = jarPath("server/build/libs/server-1.0-all.jar") - - fun jarPath(path: String): JarPath { - val realPath = root.resolve(path).also { - check(it.exists()) { "Path not found: $it" } - check(it.isRegularFile()) { "Path is not a regular file: $it" } - } - return JarPath(realPath) + fun jarPath(relative: String, parent: Path = projectRoot): JarPath { + return JarPath(parent.resolve(relative)) } - fun path(path: String): Path { - return root.resolve(path).also { + fun path(relative: String, parent: Path = projectRoot): Path { + return parent.resolve(relative).also { if (it.exists()) return@also it.parent.createDirectories() // We should create text file only if it doesn't exist. @@ -296,7 +294,7 @@ abstract class BasePluginTest { fun runner( arguments: Iterable = emptyList(), - projectDir: Path? = root, + projectDir: Path? = projectRoot, ): GradleRunner = GradleRunner.create() .forwardOutput() .withPluginClasspath() @@ -317,11 +315,9 @@ abstract class BasePluginTest { Path(gradleUserHome, "testkit") } - val testJar: Path = requireNotNull(this::class.java.classLoader.getResource("junit-3.8.2.jar")).toURI().toPath() - val artifactJar: Path = - requireNotNull(this::class.java.classLoader.getResource("test-artifact-1.0-SNAPSHOT.jar")).toURI().toPath() - val projectJar: Path = - requireNotNull(this::class.java.classLoader.getResource("test-project-1.0-SNAPSHOT.jar")).toURI().toPath() + val testJar: Path = requireResourceAsPath("junit-3.8.2.jar") + val artifactJar: Path = requireResourceAsPath("test-artifact-1.0-SNAPSHOT.jar") + val projectJar: Path = requireResourceAsPath("test-project-1.0-SNAPSHOT.jar") val shadowJar: String = """ tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name}) @@ -334,6 +330,7 @@ abstract class BasePluginTest { val commonArguments = listOf( "--warning-mode=fail", "--configuration-cache", + "--build-cache", "--stacktrace", ) @@ -358,14 +355,26 @@ abstract class BasePluginTest { } fun BuildResult.assertNoDeprecationWarnings() = apply { - output.lines().forEach { - assert(!containsDeprecationWarning(it)) - } + assertThat(output).doesNotContain( + "has been deprecated and is scheduled to be removed in Gradle", + "has been deprecated. This is scheduled to be removed in Gradle", + ) + } + + fun Assert.useAll(body: Assert.() -> Unit) = all { + body() + // Close the resource after all assertions are done. + given { it.use(block = {}) } + } + + fun Assert.taskOutcomeEquals(taskPath: String, expectedOutcome: TaskOutcome) { + return transform { it.task(taskPath)?.outcome }.isNotNull().isEqualTo(expectedOutcome) } - private fun containsDeprecationWarning(output: String): Boolean { - return output.contains("has been deprecated and is scheduled to be removed in Gradle") || - output.contains("has been deprecated. This is scheduled to be removed in Gradle") + private fun requireResourceAsPath(name: String): Path { + val resource = this::class.java.classLoader.getResource(name) + ?: throw NoSuchFileException("Resource $name not found.") + return resource.toURI().toPath() } } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt similarity index 67% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index a79f2b32c..fd7f0fe64 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -1,14 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isNotNull import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText -import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -36,11 +34,13 @@ class FilteringTest : BasePluginTest() { @Test fun includeAllDependencies() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "a.properties", - "a2.properties", - "b.properties", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "a.properties", + "a2.properties", + "b.properties", + ) + } } @Test @@ -55,13 +55,15 @@ class FilteringTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "a.properties", - "b.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "a2.properties", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "a.properties", + "b.properties", + ) + doesNotContainEntries( + "a2.properties", + ) + } } @Test @@ -106,17 +108,18 @@ class FilteringTest : BasePluginTest() { projectScriptPath.writeText(replaced) val result = run(shadowJarTask) - assertThat(result.task(shadowJarTask)).isNotNull() - .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) - assertThat(outputShadowJar).containsEntries( - "a.properties", - "a2.properties", - "b.properties", - "d.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "c.properties", - ) + assertThat(result).taskOutcomeEquals(shadowJarTask, SUCCESS) + assertThat(outputShadowJar).useAll { + containsEntries( + "a.properties", + "a2.properties", + "b.properties", + "d.properties", + ) + doesNotContainEntries( + "c.properties", + ) + } } @Test @@ -133,17 +136,18 @@ class FilteringTest : BasePluginTest() { val result = run(shadowJarTask) - assertThat(result.task(shadowJarTask)).isNotNull() - .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) - assertThat(outputShadowJar).containsEntries( - "a2.properties", - "b.properties", - "c.properties", - "d.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "a.properties", - ) + assertThat(result).taskOutcomeEquals(shadowJarTask, SUCCESS) + assertThat(outputShadowJar).useAll { + containsEntries( + "a2.properties", + "b.properties", + "c.properties", + "d.properties", + ) + doesNotContainEntries( + "a.properties", + ) + } } @Test @@ -169,16 +173,18 @@ class FilteringTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "d.properties", - "shadow/Passed.class", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "a.properties", - "a2.properties", - "b.properties", - "c.properties", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "d.properties", + "shadow/Passed.class", + ) + doesNotContainEntries( + "a.properties", + "a2.properties", + "b.properties", + "c.properties", + ) + } } @Test @@ -193,13 +199,15 @@ class FilteringTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).doesNotContainEntries( - "client/Client.class", - ) - assertThat(outputServerShadowJar).containsEntries( - "server/Server.class", - "junit/framework/Test.class", - ) + assertThat(outputServerShadowJar).useAll { + containsEntries( + "server/Server.class", + "junit/framework/Test.class", + ) + doesNotContainEntries( + "client/Client.class", + ) + } } @Test @@ -214,13 +222,15 @@ class FilteringTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).doesNotContainEntries( - "junit/framework/Test.class", - ) - assertThat(outputServerShadowJar).containsEntries( - "client/Client.class", - "server/Server.class", - ) + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } } @Test @@ -237,13 +247,15 @@ class FilteringTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "a.properties", - "b.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "a2.properties", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "a.properties", + "b.properties", + ) + doesNotContainEntries( + "a2.properties", + ) + } } @Test @@ -272,15 +284,17 @@ class FilteringTest : BasePluginTest() { } private fun commonAssertions() { - assertThat(outputShadowJar).containsEntries( - "a.properties", - "a2.properties", - "b.properties", - "c.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "d.properties", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "a.properties", + "a2.properties", + "b.properties", + "c.properties", + ) + doesNotContainEntries( + "d.properties", + ) + } } private fun publishArtifactCD(circular: Boolean = false) { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt similarity index 90% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index b0fa6f58e..167ba3626 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow -import assertk.all import assertk.assertThat import assertk.assertions.containsOnly import assertk.assertions.isEmpty @@ -33,12 +32,12 @@ class PublishingTest : BasePluginTest() { private val gmmAdapter = moshi.adapter(GradleModuleMetadata::class.java) private val pomReader = MavenXpp3Reader() - private lateinit var remoteRepo: Path + private lateinit var remoteRepoPath: Path @BeforeEach override fun setup() { super.setup() - remoteRepo = root.resolve("remote-maven-repo") + remoteRepoPath = projectRoot.resolve("remote-maven-repo") settingsScriptPath.appendText("rootProject.name = 'maven'" + System.lineSeparator()) } @@ -122,18 +121,18 @@ class PublishingTest : BasePluginTest() { publish() - val publishedJar = repoJarPath("shadow/maven-all/1.0/maven-all-1.0.jar") - assertThat(publishedJar).containsEntries( - "aa.properties", - "aa2.properties", - ) - assertThat(publishedJar).doesNotContainEntries( - "a.properties", - "a2.properties", - "b.properties", - "bb.properties", - ) - + assertThat(repoJarPath("shadow/maven-all/1.0/maven-all-1.0.jar")).useAll { + containsEntries( + "aa.properties", + "aa2.properties", + ) + doesNotContainEntries( + "a.properties", + "a2.properties", + "b.properties", + "bb.properties", + ) + } assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) } @@ -165,8 +164,12 @@ class PublishingTest : BasePluginTest() { publish() val entries = arrayOf("a.properties", "a2.properties", "b.properties") - assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0.jar")).doesNotContainEntries(*entries) - assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0-all.jar")).containsEntries(*entries) + assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0.jar")).useAll { + doesNotContainEntries(*entries) + } + assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0-all.jar")).useAll { + containsEntries(*entries) + } pomReader.read(repoPath("com/acme/maven/1.0/maven-1.0.pom")).let { pomContents -> assertThat(pomContents.dependencies.size).isEqualTo(2) @@ -211,15 +214,15 @@ class PublishingTest : BasePluginTest() { } } - private fun repoPath(path: String): Path { - return remoteRepo.resolve(path).also { + private fun repoPath(relative: String): Path { + return remoteRepoPath.resolve(relative).also { check(it.exists()) { "Path not found: $it" } check(it.isRegularFile()) { "Path is not a regular file: $it" } } } - private fun repoJarPath(path: String): JarPath { - return JarPath(repoPath(path)) + private fun repoJarPath(relative: String): JarPath { + return JarPath(remoteRepoPath.resolve(relative)) } private fun publish(): BuildResult = run("publish") @@ -253,7 +256,7 @@ class PublishingTest : BasePluginTest() { } repositories { maven { - url = '${remoteRepo.toUri()}' + url = '${remoteRepoPath.toUri()}' } } } @@ -278,7 +281,7 @@ class PublishingTest : BasePluginTest() { } private fun assertShadowJarCommon(jarPath: JarPath) { - assertThat(jarPath).all { + assertThat(jarPath).useAll { containsEntries( "a.properties", "a2.properties", @@ -290,7 +293,7 @@ class PublishingTest : BasePluginTest() { } private companion object { - fun MavenXpp3Reader.read(path: Path): Model = read(path.inputStream()) + fun MavenXpp3Reader.read(path: Path): Model = path.inputStream().use { read(it) } fun JsonAdapter.fromJson(path: Path): T = requireNotNull(fromJson(path.readText())) } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt similarity index 54% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 8571c350d..fa1f6f380 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -4,17 +4,19 @@ import assertk.assertFailure import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf +import assertk.fail import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import java.net.URLClassLoader import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test +import org.opentest4j.AssertionFailedError class RelocationTest : BasePluginTest() { - @Test fun defaultEnableRelocation() { projectScriptPath.appendText( @@ -27,26 +29,29 @@ class RelocationTest : BasePluginTest() { } """.trimIndent(), ) + run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "META-INF/MANIFEST.MF", - "shadow/junit/textui/ResultPrinter.class", - "shadow/junit/textui/TestRunner.class", - "shadow/junit/framework/Assert.class", - "shadow/junit/framework/AssertionFailedError.class", - "shadow/junit/framework/ComparisonCompactor.class", - "shadow/junit/framework/ComparisonFailure.class", - "shadow/junit/framework/Protectable.class", - "shadow/junit/framework/Test.class", - "shadow/junit/framework/TestCase.class", - "shadow/junit/framework/TestFailure.class", - "shadow/junit/framework/TestListener.class", - "shadow/junit/framework/TestResult$1.class", - "shadow/junit/framework/TestResult.class", - "shadow/junit/framework/TestSuite$1.class", - "shadow/junit/framework/TestSuite.class", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "META-INF/MANIFEST.MF", + "shadow/junit/textui/ResultPrinter.class", + "shadow/junit/textui/TestRunner.class", + "shadow/junit/framework/Assert.class", + "shadow/junit/framework/AssertionFailedError.class", + "shadow/junit/framework/ComparisonCompactor.class", + "shadow/junit/framework/ComparisonFailure.class", + "shadow/junit/framework/Protectable.class", + "shadow/junit/framework/Test.class", + "shadow/junit/framework/TestCase.class", + "shadow/junit/framework/TestFailure.class", + "shadow/junit/framework/TestListener.class", + "shadow/junit/framework/TestResult$1.class", + "shadow/junit/framework/TestResult.class", + "shadow/junit/framework/TestSuite$1.class", + "shadow/junit/framework/TestSuite.class", + ) + } } @Issue( @@ -71,44 +76,44 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "META-INF/MANIFEST.MF", - "a/ResultPrinter.class", - "a/TestRunner.class", - "b/Assert.class", - "b/AssertionFailedError.class", - "b/ComparisonCompactor.class", - "b/ComparisonFailure.class", - "b/Protectable.class", - "b/Test.class", - "b/TestCase.class", - "b/TestFailure.class", - "b/TestListener.class", - "b/TestResult\$1.class", - "b/TestResult.class", - "b/TestSuite\$1.class", - "b/TestSuite.class", - ) - - assertThat(outputShadowJar).doesNotContainEntries( - "junit/textui/ResultPrinter.class", - "junit/textui/TestRunner.class", - "junit/framework/Assert.class", - "junit/framework/AssertionFailedError.class", - "junit/framework/ComparisonCompactor.class", - "junit/framework/ComparisonFailure.class", - "junit/framework/Protectable.class", - "junit/framework/Test.class", - "junit/framework/TestCase.class", - "junit/framework/TestFailure.class", - "junit/framework/TestListener.class", - "junit/framework/TestResult\$1.class", - "junit/framework/TestResult.class", - "junit/framework/TestSuite\$1.class", - "junit/framework/TestSuite.class", - ) - assertThat(outputShadowJar.manifest.mainAttributes.getValue("TEST-VALUE")) - .isEqualTo("FOO") + assertThat(outputShadowJar).useAll { + containsEntries( + "META-INF/MANIFEST.MF", + "a/ResultPrinter.class", + "a/TestRunner.class", + "b/Assert.class", + "b/AssertionFailedError.class", + "b/ComparisonCompactor.class", + "b/ComparisonFailure.class", + "b/Protectable.class", + "b/Test.class", + "b/TestCase.class", + "b/TestFailure.class", + "b/TestListener.class", + "b/TestResult\$1.class", + "b/TestResult.class", + "b/TestSuite\$1.class", + "b/TestSuite.class", + ) + doesNotContainEntries( + "junit/textui/ResultPrinter.class", + "junit/textui/TestRunner.class", + "junit/framework/Assert.class", + "junit/framework/AssertionFailedError.class", + "junit/framework/ComparisonCompactor.class", + "junit/framework/ComparisonFailure.class", + "junit/framework/Protectable.class", + "junit/framework/Test.class", + "junit/framework/TestCase.class", + "junit/framework/TestFailure.class", + "junit/framework/TestListener.class", + "junit/framework/TestResult\$1.class", + "junit/framework/TestResult.class", + "junit/framework/TestSuite\$1.class", + "junit/framework/TestSuite.class", + ) + getMainAttr("TEST-VALUE").isEqualTo("FOO") + } } @Test @@ -131,32 +136,33 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "a/ResultPrinter.class", - "b/Test.class", - "b/TestCase.class", - "b/TestFailure.class", - "b/TestListener.class", - "b/TestResult\$1.class", - "b/TestResult.class", - "b/TestSuite\$1.class", - "b/TestSuite.class", - "junit/textui/TestRunner.class", - "junit/framework/Assert.class", - "junit/framework/AssertionFailedError.class", - "junit/framework/ComparisonCompactor.class", - "junit/framework/ComparisonFailure.class", - "junit/framework/Protectable.class", - ) - - assertThat(outputShadowJar).doesNotContainEntries( - "a/TestRunner.class", - "b/Assert.class", - "b/AssertionFailedError.class", - "b/ComparisonCompactor.class", - "b/ComparisonFailure.class", - "b/Protectable.class", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "a/ResultPrinter.class", + "b/Test.class", + "b/TestCase.class", + "b/TestFailure.class", + "b/TestListener.class", + "b/TestResult\$1.class", + "b/TestResult.class", + "b/TestSuite\$1.class", + "b/TestSuite.class", + "junit/textui/TestRunner.class", + "junit/framework/Assert.class", + "junit/framework/AssertionFailedError.class", + "junit/framework/ComparisonCompactor.class", + "junit/framework/ComparisonFailure.class", + "junit/framework/Protectable.class", + ) + doesNotContainEntries( + "a/TestRunner.class", + "b/Assert.class", + "b/AssertionFailedError.class", + "b/ComparisonCompactor.class", + "b/ComparisonFailure.class", + "b/Protectable.class", + ) + } } @Issue( @@ -192,27 +198,27 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "shadow/ShadowTest.class", - "shadow/junit/Test.class", - "shadow/junit", - ) - - assertThat(outputShadowJar).doesNotContainEntries( - "junit/framework", - "junit/framework/Test.class", - ) - - val classLoader = URLClassLoader( - arrayOf(outputShadowJar.toUri().toURL()), - ClassLoader.getSystemClassLoader().parent, - ) - assertFailure { - // check that the class can be loaded. If the file was not relocated properly, we should get a NoDefClassFound - // Isolated class loader with only the JVM system jars and the output jar from the test project - classLoader.loadClass("shadow.ShadowTest") - error("Should not reach here.") - }.isInstanceOf(IllegalStateException::class) + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/ShadowTest.class", + "shadow/junit/Test.class", + "shadow/junit", + ) + doesNotContainEntries( + "junit/framework", + "junit/framework/Test.class", + ) + } + + val url = outputShadowJar.use { it.toUri().toURL() } + URLClassLoader(arrayOf(url), ClassLoader.getSystemClassLoader().parent).use { classLoader -> + assertFailure { + // check that the class can be loaded. If the file was not relocated properly, we should get a NoDefClassFound + // Isolated class loader with only the JVM system jars and the output jar from the test project + classLoader.loadClass("shadow.ShadowTest") + fail("Should not reach here.") + }.isInstanceOf(AssertionFailedError::class) + } } @Test @@ -272,15 +278,16 @@ class RelocationTest : BasePluginTest() { run(":app:$SHADOW_JAR_TASK_NAME") - val appOutput = jarPath("app/build/libs/app-all.jar") - assertThat(appOutput).containsEntries( - "TEST", - "APP-TEST", - "test.properties", - "app/core/Core.class", - "app/App.class", - "app/junit/framework/Test.class", - ) + assertThat(jarPath("app/build/libs/app-all.jar")).useAll { + containsEntries( + "TEST", + "APP-TEST", + "test.properties", + "app/core/Core.class", + "app/App.class", + "app/junit/framework/Test.class", + ) + } } @Issue( @@ -316,16 +323,18 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "bar/Foo.class", - "bar/foo.properties", - "bar/dep.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "foo/Foo.class", - "foo/foo.properties", - "foo/dep.properties", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "bar/Foo.class", + "bar/foo.properties", + "bar/dep.properties", + ) + doesNotContainEntries( + "foo/Foo.class", + "foo/foo.properties", + "foo/dep.properties", + ) + } } @Issue( diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt similarity index 74% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt index bb1343508..46570901d 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -18,14 +18,14 @@ import kotlin.io.path.readText import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin import org.gradle.testfixtures.ProjectBuilder -import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.EnabledForJreRange import org.junit.jupiter.api.condition.JRE class ShadowPluginTest : BasePluginTest() { - @Test fun applyPlugin() { val projectName = "my-shadow" @@ -120,15 +120,13 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - val outputShadowJar = jarPath("build/libs/shadow.jar") - - assertThat(outputShadowJar).containsEntries( - "shadow/Passed.class", - "junit/framework/Test.class", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "/", - ) + assertThat(jarPath("build/libs/shadow.jar")).useAll { + containsEntries( + "shadow/Passed.class", + "junit/framework/Test.class", + ) + doesNotContainEntries("/") + } } @Test @@ -137,11 +135,13 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).containsEntries( - "client/Client.class", - "server/Server.class", - "junit/framework/Test.class", - ) + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + "junit/framework/Test.class", + ) + } } /** @@ -168,13 +168,15 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).containsEntries( - "client/Client.class", - "server/Server.class", - ) - assertThat(outputServerShadowJar).doesNotContainEntries( - "junit/framework/Test.class", - ) + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } } /** @@ -194,13 +196,15 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).containsEntries( - "server/Server.class", - "junit/framework/Test.class", - ) - assertThat(outputServerShadowJar).doesNotContainEntries( - "client/Client.class", - ) + assertThat(outputServerShadowJar).useAll { + containsEntries( + "server/Server.class", + "junit/framework/Test.class", + ) + doesNotContainEntries( + "client/Client.class", + ) + } } /** @@ -219,10 +223,12 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).containsEntries( - "client/Client.class", - "server/Server.class", - ) + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + ) + } } /** @@ -250,11 +256,13 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).containsEntries( - "client/Client.class", - "server/Server.class", - "junit/framework/TestCase.class", - ) + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + "junit/framework/TestCase.class", + ) + } path("client/src/main/java/client/Client.java").writeText( """ @@ -264,13 +272,13 @@ class ShadowPluginTest : BasePluginTest() { ) run(serverShadowJarTask) - // TODO: I don't think junit classes should be in the output jar, but it's the test case - // from https://github.com/GradleUp/shadow/pull/420, need to investigate more... - assertThat(outputServerShadowJar).containsEntries( - "client/Client.class", - "server/Server.class", - "junit/framework/TestCase.class", - ) + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + "junit/framework/TestCase.class", + ) + } } /** @@ -284,17 +292,18 @@ class ShadowPluginTest : BasePluginTest() { run(":impl:$SHADOW_JAR_TASK_NAME") - val implOutput = jarPath("impl/build/libs/impl-all.jar") - assertThat(implOutput).containsEntries( - "impl/SimpleEntity.class", - "api/Entity.class", - "api/UnusedEntity.class", - "lib/LibEntity.class", - ) - assertThat(implOutput).doesNotContainEntries( - "junit/framework/Test.class", - "lib/UnusedLibEntity.class", - ) + assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { + containsEntries( + "impl/SimpleEntity.class", + "api/Entity.class", + "api/UnusedEntity.class", + "lib/LibEntity.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + "lib/UnusedLibEntity.class", + ) + } } /** @@ -317,17 +326,18 @@ class ShadowPluginTest : BasePluginTest() { run(":impl:$SHADOW_JAR_TASK_NAME") - val implOutput = jarPath("impl/build/libs/impl-all.jar") - assertThat(implOutput).containsEntries( - "impl/SimpleEntity.class", - "api/Entity.class", - "api/UnusedEntity.class", - "lib/LibEntity.class", - "lib/UnusedLibEntity.class", - ) - assertThat(implOutput).doesNotContainEntries( - "junit/framework/Test.class", - ) + assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { + containsEntries( + "impl/SimpleEntity.class", + "api/Entity.class", + "api/UnusedEntity.class", + "lib/LibEntity.class", + "lib/UnusedLibEntity.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } } @Test @@ -336,21 +346,22 @@ class ShadowPluginTest : BasePluginTest() { run(":server:jar") - val serverOutput = jarPath("server/build/libs/server-1.0.jar") - assertThat(serverOutput).containsEntries( - "server/Server.class", - ) - assertThat(serverOutput).doesNotContainEntries( - "client/Client.class", - "junit/framework/Test.class", - "client/junit/framework/Test.class", - ) - - val clientOutput = jarPath("client/build/libs/client-all.jar") - assertThat(clientOutput).containsEntries( - "client/Client.class", - "client/junit/framework/Test.class", - ) + assertThat(jarPath("server/build/libs/server-1.0.jar")).useAll { + containsEntries( + "server/Server.class", + ) + doesNotContainEntries( + "client/Client.class", + "junit/framework/Test.class", + "client/junit/framework/Test.class", + ) + } + assertThat(jarPath("client/build/libs/client-all.jar")).useAll { + containsEntries( + "client/Client.class", + "client/junit/framework/Test.class", + ) + } } @Test @@ -359,20 +370,22 @@ class ShadowPluginTest : BasePluginTest() { run(serverShadowJarTask) - assertThat(outputServerShadowJar).containsEntries( - "client/Client.class", - "client/junit/framework/Test.class", - "server/Server.class", - ) - assertThat(outputServerShadowJar).doesNotContainEntries( - "junit/framework/Test.class", - ) - - val clientOutput = jarPath("client/build/libs/client-all.jar") - assertThat(clientOutput).containsEntries( - "client/Client.class", - "client/junit/framework/Test.class", - ) + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "client/junit/framework/Test.class", + "server/Server.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } + assertThat(jarPath("client/build/libs/client-all.jar")).useAll { + containsEntries( + "client/Client.class", + "client/junit/framework/Test.class", + ) + } } @Test @@ -405,18 +418,20 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "shadow/Passed.class", - "a.properties", - "META-INF/a.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "META-INF/INDEX.LIST", - "META-INF/a.SF", - "META-INF/a.DSA", - "META-INF/a.RSA", - "module-info.class", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Passed.class", + "a.properties", + "META-INF/a.properties", + ) + doesNotContainEntries( + "META-INF/INDEX.LIST", + "META-INF/a.SF", + "META-INF/a.DSA", + "META-INF/a.RSA", + "module-info.class", + ) + } } @Test @@ -432,13 +447,15 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "a.properties", - "a2.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "b.properties", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "a.properties", + "a2.properties", + ) + doesNotContainEntries( + "b.properties", + ) + } } @Test @@ -475,12 +492,14 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "api.properties", - "implementation.properties", - "runtimeOnly.properties", - "implementation-dep.properties", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "api.properties", + "implementation.properties", + "runtimeOnly.properties", + "implementation-dep.properties", + ) + } } @Test @@ -496,13 +515,15 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).containsEntries( - "a.properties", - "a2.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "b.properties", - ) + assertThat(outputShadowJar).useAll { + containsEntries( + "a.properties", + "a2.properties", + ) + doesNotContainEntries( + "b.properties", + ) + } } @Test @@ -528,7 +549,8 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar.entries().toList().size).isEqualTo(2) + val entries = outputShadowJar.use { it.entries().toList() } + assertThat(entries.size).isEqualTo(2) } @Test @@ -543,7 +565,8 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar.manifest.mainAttributes.getValue("Class-Path")).isNull() + val value = outputShadowJar.use { it.getMainAttr("Class-Path") } + assertThat(value).isNull() } @Issue( @@ -566,8 +589,8 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar.manifest.mainAttributes.getValue("Class-Path")) - .isEqualTo("/libs/a.jar junit-3.8.2.jar") + val value = outputShadowJar.use { it.getMainAttr("Class-Path") } + assertThat(value).isEqualTo("/libs/a.jar junit-3.8.2.jar") } @Issue( @@ -585,8 +608,8 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar.manifest.mainAttributes.getValue("Class-Path")) - .isEqualTo("junit-3.8.2.jar") + val value = outputShadowJar.use { it.getMainAttr("Class-Path") } + assertThat(value).isEqualTo("junit-3.8.2.jar") } @Issue( @@ -740,7 +763,7 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - val entries = outputShadowJar.entries().toList() + val entries = outputShadowJar.use { it.entries().toList() } assertThat(entries.count { it.name.endsWith(".class") }).isEqualTo(1) } @@ -767,10 +790,44 @@ class ShadowPluginTest : BasePluginTest() { val result = run(testShadowJarTask) - assertThat(result.task(":$testShadowJarTask")).isNotNull() - .transform { it.outcome }.isEqualTo(TaskOutcome.SUCCESS) - val testJar = jarPath("build/libs/shadow-1.0-tests.jar") - assertThat(testJar.getEntry("junit")).isNotNull() + assertThat(result).taskOutcomeEquals(":$testShadowJarTask", SUCCESS) + val junitEntry = jarPath("build/libs/shadow-1.0-tests.jar").use { it.getEntry("junit") } + assertThat(junitEntry).isNotNull() + } + + @Test + fun configurationCachingOfConfigurationsIsUpToDate() { + settingsScriptPath.appendText( + """ + include 'lib' + """.trimIndent(), + ) + projectScriptPath.writeText("") + + path("lib/src/main/java/lib/Lib.java").writeText( + """ + package lib; + public class Lib {} + """.trimIndent(), + ) + path("lib/build.gradle").writeText( + """ + ${getDefaultProjectBuildScript()} + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + configurations = [project.configurations.compileClasspath] + } + """.trimIndent(), + ) + + val libShadowJarTask = ":lib:$SHADOW_JAR_TASK_NAME" + run(libShadowJarTask) + val result = run(libShadowJarTask) + + assertThat(result).taskOutcomeEquals(libShadowJarTask, UP_TO_DATE) + assertThat(result.output).contains("Reusing configuration cache.") } private fun writeShadowedClientAndServerModules() { diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt new file mode 100644 index 000000000..077d7b579 --- /dev/null +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt @@ -0,0 +1,46 @@ +package com.github.jengelman.gradle.plugins.shadow.caching + +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.isEmpty +import assertk.assertions.isInstanceOf +import com.github.jengelman.gradle.plugins.shadow.BasePluginTest +import java.nio.file.NoSuchFileException +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.isDirectory +import kotlin.io.path.name +import kotlin.io.path.walk +import org.gradle.testkit.runner.TaskOutcome +import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE +import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE + +abstract class BaseCachingTest : BasePluginTest() { + fun assertFirstExecutionSuccess() { + // task was executed and not pulled from cache + assertRunWithOutcome(SUCCESS) + } + + /** + * This should be called after [assertFirstExecutionSuccess] to ensure that the shadowJar task is cached. + */ + fun assertExecutionsAreCachedAndUpToDate() { + run("clean") + // Make sure the output shadow jar has been deleted. + assertFailure { outputShadowJar.close() }.isInstanceOf(NoSuchFileException::class) + @OptIn(ExperimentalPathApi::class) + val buildDirs = projectRoot.walk().filter { it.isDirectory() && it.name == "build" } + // Make sure build folders are deleted by clean task. + assertThat(buildDirs).isEmpty() + + // check that shadowJar pulls from cache in the original directory + assertRunWithOutcome(FROM_CACHE) + // check that shadowJar pulls from cache in a different directory + assertRunWithOutcome(UP_TO_DATE) + } + + private fun assertRunWithOutcome(expectedOutcome: TaskOutcome) { + val result = run(shadowJarTask) + assertThat(result).taskOutcomeEquals(shadowJarTask, expectedOutcome) + } +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt similarity index 90% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt index e1558090c..3921a87a1 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt @@ -4,7 +4,6 @@ import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries -import com.github.jengelman.gradle.plugins.shadow.util.useAll import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test @@ -23,7 +22,7 @@ class MinimizationCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "server/Server.class", @@ -42,7 +41,7 @@ class MinimizationCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "server/Server.class", @@ -53,7 +52,7 @@ class MinimizationCachingTest : BaseCachingTest() { ) } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries( "server/Server.class", diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt similarity index 90% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index a62b217d5..43a05f563 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries -import com.github.jengelman.gradle.plugins.shadow.util.useAll import kotlin.io.path.appendText import org.junit.jupiter.api.Test @@ -22,7 +21,7 @@ class RelocationCachingTest : BaseCachingTest() { ) writeMainClass(withImports = true) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -38,7 +37,7 @@ class RelocationCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -49,7 +48,7 @@ class RelocationCachingTest : BaseCachingTest() { ) } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt similarity index 86% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 3c99cc192..3af4c28ed 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -4,11 +4,9 @@ import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import com.github.jengelman.gradle.plugins.shadow.util.isRegular -import com.github.jengelman.gradle.plugins.shadow.util.useAll import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText -import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE import org.junit.jupiter.api.Test class ShadowJarCachingTest : BaseCachingTest() { @@ -25,14 +23,14 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertShadowJarExecutes() - assertShadowJarIsCachedAndRelocatable() + assertFirstExecutionSuccess() + assertExecutionsAreCachedAndUpToDate() val replaced = projectScriptPath.readText().lines().filter { it != fromJar(projectJar) }.joinToString(System.lineSeparator()) projectScriptPath.writeText(replaced) - assertShadowJarExecutes() + assertFirstExecutionSuccess() } @Test @@ -45,7 +43,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() projectScriptPath.appendText( """ @@ -54,8 +52,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } """.trimIndent(), ) - // TODO: need to investigate why secondOutcome is FROM_CACHE instead of UP_TO_DATE. - assertShadowJarIsCachedAndRelocatable(secondOutcome = FROM_CACHE) + assertExecutionsAreCachedAndUpToDate() assertThat(jarPath("build/libs/foo-1.0-all.jar")).isRegular() } @@ -87,7 +84,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "server/Server.class", @@ -105,7 +102,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } """.trimIndent(), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "server/Server.class", @@ -116,7 +113,7 @@ class ShadowJarCachingTest : BaseCachingTest() { ) } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries( "server/Server.class", @@ -140,7 +137,7 @@ class ShadowJarCachingTest : BaseCachingTest() { writeMainClass(withImports = true) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -157,7 +154,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } """.trimIndent(), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -167,7 +164,7 @@ class ShadowJarCachingTest : BaseCachingTest() { ) } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt similarity index 76% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt index c749a6582..63257e886 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt @@ -3,45 +3,20 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.NoOpTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.useAll import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText import org.junit.jupiter.api.Test class TransformCachingTest : BaseCachingTest() { - @Test - fun shadowJarIsNotCachedWhenCustomTransformsAreUsed() { - writeMainClass() - projectScriptPath.appendText( - """ - $shadowJar { - // Use NoOpTransformer to mock a custom transformer here. - transform(${NoOpTransformer::class.java.name}.INSTANCE) - } - """.trimIndent(), - ) - - assertShadowJarExecutes() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } - - assertShadowJarExecutes() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } - } - @Test fun shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer() { writeMainClass() - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } @@ -54,12 +29,12 @@ class TransformCachingTest : BaseCachingTest() { ), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } @@ -67,12 +42,12 @@ class TransformCachingTest : BaseCachingTest() { val replaced = projectScriptPath.readText().replace("META-INF/foo", "META-INF/bar") projectScriptPath.writeText(replaced) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } @@ -83,7 +58,7 @@ class TransformCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.properties").writeText("foo=bar") writeMainClass() - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } @@ -96,12 +71,12 @@ class TransformCachingTest : BaseCachingTest() { ), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class", "foo/bar.properties") } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class", "foo/bar.properties") } @@ -111,12 +86,12 @@ class TransformCachingTest : BaseCachingTest() { val replaced = projectScriptPath.readText().replace("foo/bar.properties", "foo/baz.properties") projectScriptPath.writeText(replaced) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class", "foo/baz.properties") } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class", "foo/baz.properties") } @@ -127,7 +102,7 @@ class TransformCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.xml").writeText("bar") writeMainClass() - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } @@ -140,12 +115,12 @@ class TransformCachingTest : BaseCachingTest() { ), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class", "foo/bar.xml") } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class", "foo/bar.xml") } @@ -155,12 +130,12 @@ class TransformCachingTest : BaseCachingTest() { val replaced = projectScriptPath.readText().replace("foo/bar.xml", "foo/baz.xml") projectScriptPath.writeText(replaced) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class", "foo/baz.xml") } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class", "foo/baz.xml") } @@ -170,7 +145,7 @@ class TransformCachingTest : BaseCachingTest() { fun shadowJarIsCachedCorrectlyWhenUsingGroovyExtensionModuleTransformer() { writeMainClass() - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } @@ -179,12 +154,12 @@ class TransformCachingTest : BaseCachingTest() { transform(), ) - assertShadowJarExecutes() + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } - assertShadowJarIsCachedAndRelocatable() + assertExecutionsAreCachedAndUpToDate() assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt similarity index 80% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 24e12cd09..04807f7f4 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -25,8 +25,8 @@ class AppendingTransformerTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar.getContent(ENTRY_TEST_PROPERTIES).trimIndent()) - .isEqualTo(CONTENT_ONE_TWO) + val content = outputShadowJar.use { it.getContent(ENTRY_TEST_PROPERTIES) }.trimIndent() + assertThat(content).isEqualTo(CONTENT_ONE_TWO) } @Test @@ -48,7 +48,7 @@ class AppendingTransformerTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar.getContent(ENTRY_TEST_PROPERTIES).trimIndent()) - .isEqualTo(CONTENT_ONE_TWO) + val content = outputShadowJar.use { it.getContent(ENTRY_TEST_PROPERTIES) }.trimIndent() + assertThat(content).isEqualTo(CONTENT_ONE_TWO) } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt similarity index 86% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 557275a4d..b4ee8444b 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -4,8 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.BasePluginTest import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder import java.nio.file.Path -sealed class BaseTransformerTest : BasePluginTest() { - +abstract class BaseTransformerTest : BasePluginTest() { fun buildJarOne( builder: JarBuilder.() -> Unit = { insert(ENTRY_SERVICES_SHADE, CONTENT_ONE) @@ -24,8 +23,8 @@ sealed class BaseTransformerTest : BasePluginTest() { return buildJar("two.jar", builder) } - inline fun buildJar(path: String, builder: JarBuilder.() -> Unit): Path { - return JarBuilder(path(path)).apply(builder).write() + inline fun buildJar(relative: String, builder: JarBuilder.() -> Unit): Path { + return JarBuilder(path("temp/$relative")).apply(builder).write() } protected companion object { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt similarity index 75% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index 8838f4cd8..c373895f5 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -11,7 +11,6 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR import java.nio.file.Path -import java.util.Properties import kotlin.io.path.appendText import org.junit.jupiter.api.Test @@ -26,8 +25,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { run(shadowJarTask) - val props = outputShadowJar.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR).toProperties() - commonAssertions(props) + commonAssertions(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) } @Test @@ -43,8 +41,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { run(shadowJarTask) - val props = outputShadowJar.getContent(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR).toProperties() - commonAssertions(props) + commonAssertions(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR) } @Test @@ -60,15 +57,14 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { run(shadowJarTask) - val props = outputShadowJar.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR).toProperties() - commonAssertions(props) + commonAssertions(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) } private fun buildJarFoo( - path: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + entry: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, ): Path = buildJar("foo.jar") { insert( - path, + entry, """ $KEY_MODULE_NAME=foo $KEY_MODULE_VERSION=1.0.5 @@ -79,10 +75,10 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { } private fun buildJarBar( - path: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + entry: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, ): Path = buildJar("bar.jar") { insert( - path, + entry, """ $KEY_MODULE_NAME=bar $KEY_MODULE_VERSION=2.3.5 @@ -92,19 +88,21 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { ) } + private fun commonAssertions(entry: String) { + val properties = outputShadowJar.use { it.getContent(entry) }.toProperties() + + assertThat(properties.getProperty(KEY_MODULE_NAME)).isEqualTo(MERGED_MODULE_NAME) + assertThat(properties.getProperty(KEY_MODULE_VERSION)).isEqualTo(MERGED_MODULE_VERSION) + assertThat(properties.getProperty(KEY_EXTENSION_CLASSES)) + .isEqualTo("$EXTENSION_CLASSES_FOO,$EXTENSION_CLASSES_BAR") + assertThat(properties.getProperty(KEY_STATIC_EXTENSION_CLASSES)) + .isEqualTo("$STATIC_EXTENSION_CLASSES_FOO,$STATIC_EXTENSION_CLASSES_BAR") + } + private companion object { const val EXTENSION_CLASSES_FOO = "com.acme.foo.FooExtension,com.acme.foo.BarExtension" const val EXTENSION_CLASSES_BAR = "com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension" const val STATIC_EXTENSION_CLASSES_FOO = "com.acme.foo.FooStaticExtension" const val STATIC_EXTENSION_CLASSES_BAR = "com.acme.bar.SomeStaticExtension" - - fun commonAssertions(properties: Properties) { - assertThat(properties.getProperty(KEY_MODULE_NAME)).isEqualTo(MERGED_MODULE_NAME) - assertThat(properties.getProperty(KEY_MODULE_VERSION)).isEqualTo(MERGED_MODULE_VERSION) - assertThat(properties.getProperty(KEY_EXTENSION_CLASSES)) - .isEqualTo("$EXTENSION_CLASSES_FOO,$EXTENSION_CLASSES_BAR") - assertThat(properties.getProperty(KEY_STATIC_EXTENSION_CLASSES)) - .isEqualTo("$STATIC_EXTENSION_CLASSES_FOO,$STATIC_EXTENSION_CLASSES_BAR") - } } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt similarity index 72% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 65923952a..8fef8b325 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -3,12 +3,12 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test class ServiceFileTransformerTest : BaseTransformerTest() { - @Test fun serviceResourceTransformer() { projectScriptPath.appendText( @@ -22,8 +22,10 @@ class ServiceFileTransformerTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar.getContent(ENTRY_SERVICES_SHADE)).isEqualTo(CONTENT_ONE_TWO) - assertThat(outputShadowJar.getContent(ENTRY_SERVICES_FOO)).isEqualTo("one") + assertThat(outputShadowJar).useAll { + getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) + getContent(ENTRY_SERVICES_FOO).isEqualTo("one") + } } @Test @@ -45,7 +47,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar.getContent(ENTRY_FOO_SHADE)).isEqualTo(CONTENT_ONE_TWO) + val content = outputShadowJar.use { it.getContent(ENTRY_FOO_SHADE) } + assertThat(content).isEqualTo(CONTENT_ONE_TWO) } @Test @@ -63,8 +66,10 @@ class ServiceFileTransformerTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar.getContent(ENTRY_SERVICES_SHADE)).isEqualTo(CONTENT_ONE_TWO) - assertThat(outputShadowJar.getContent(ENTRY_SERVICES_FOO)).isEqualTo("one") + assertThat(outputShadowJar).useAll { + getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) + getContent(ENTRY_SERVICES_FOO).isEqualTo("one") + } } @Test @@ -119,31 +124,28 @@ class ServiceFileTransformerTest : BaseTransformerTest() { run(shadowJarTask) - val text1 = outputShadowJar.getContent("META-INF/services/java.sql.Driver") - assertThat(text1).isEqualTo( - """ - oracle.jdbc.OracleDriver - myapache.hive.jdbc.HiveDriver - myapache.derby.jdbc.AutoloadedDriver - com.mysql.jdbc.Driver - """.trimIndent(), - ) - - val text2 = outputShadowJar.getContent("META-INF/services/myapache.axis.components.compiler.Compiler") - assertThat(text2).isEqualTo( - """ - myapache.axis.components.compiler.Javac - org.apache.axis.components.compiler.Jikes - """.trimIndent(), - ) - - val text3 = outputShadowJar.getContent("META-INF/services/org.apache.commons.logging.LogFactory") - assertThat(text3).isEqualTo( - """ - myapache.commons.logging.impl.LogFactoryImpl - org.mortbay.log.Factory - """.trimIndent(), - ) + assertThat(outputShadowJar).useAll { + getContent("META-INF/services/java.sql.Driver").isEqualTo( + """ + oracle.jdbc.OracleDriver + myapache.hive.jdbc.HiveDriver + myapache.derby.jdbc.AutoloadedDriver + com.mysql.jdbc.Driver + """.trimIndent(), + ) + getContent("META-INF/services/myapache.axis.components.compiler.Compiler").isEqualTo( + """ + myapache.axis.components.compiler.Javac + org.apache.axis.components.compiler.Jikes + """.trimIndent(), + ) + getContent("META-INF/services/org.apache.commons.logging.LogFactory").isEqualTo( + """ + myapache.commons.logging.impl.LogFactoryImpl + org.mortbay.log.Factory + """.trimIndent(), + ) + } } @Test @@ -165,7 +167,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar.getContent(ENTRY_FOO_SHADE)).isEqualTo(CONTENT_ONE_TWO) + val content = outputShadowJar.use { it.getContent(ENTRY_FOO_SHADE) } + assertThat(content).isEqualTo(CONTENT_ONE_TWO) } @Issue( @@ -199,7 +202,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar.getContent(servicesShadowEntry)) - .isEqualTo(CONTENT_THREE + "\n" + CONTENT_ONE_TWO) + val content = outputShadowJar.use { it.getContent(servicesShadowEntry) } + assertThat(content).isEqualTo(CONTENT_THREE + "\n" + CONTENT_ONE_TWO) } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt similarity index 76% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 425828c5d..551578cce 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -6,6 +6,7 @@ import assertk.assertions.isNotNull import assertk.assertions.isNull import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.isRegular +import java.util.jar.Attributes import kotlin.io.path.appendText import kotlin.io.path.writeText import kotlin.reflect.KClass @@ -14,7 +15,6 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource class TransformersTest : BaseTransformerTest() { - @Test fun manifestRetained() { writeMainClass() @@ -31,10 +31,10 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) - val mf = outputShadowJar.manifest - assertThat(mf).isNotNull() - assertThat(mf.mainAttributes.getValue("Test-Entry")).isEqualTo("PASSED") - assertThat(mf.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") + commonAssertions { + assertThat(getValue("Test-Entry")).isEqualTo("PASSED") + assertThat(getValue("Main-Class")).isEqualTo("shadow.Main") + } } @Test @@ -45,11 +45,7 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) - val mf = outputShadowJar.manifest - assertThat(mf).isNotNull() - assertThat(mf.mainAttributes.getValue("Test-Entry")).isEqualTo("PASSED") - assertThat(mf.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") - assertThat(mf.mainAttributes.getValue("New-Entry")).isEqualTo("NEW") + commonAssertions() } @Test @@ -81,7 +77,8 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar.getContent(propertiesXml).trimIndent()).isEqualTo( + val content = outputShadowJar.use { it.getContent(propertiesXml) }.trimIndent() + assertThat(content).isEqualTo( """ @@ -101,17 +98,30 @@ class TransformersTest : BaseTransformerTest() { run("jar", shadowJarTask) - val mf1 = outputShadowJar.manifest - assertThat(mf1).isNotNull() - assertThat(mf1.mainAttributes.getValue("Test-Entry")).isEqualTo("PASSED") - assertThat(mf1.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") - assertThat(mf1.mainAttributes.getValue("New-Entry")).isEqualTo("NEW") - - val mf2 = jarPath("build/libs/shadow-1.0.jar").manifest - assertThat(mf2).isNotNull() - assertThat(mf2.mainAttributes.getValue("Test-Entry")).isEqualTo("FAILED") - assertThat(mf2.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") - assertThat(mf2.mainAttributes.getValue("New-Entry")).isNull() + commonAssertions() + + val mf = jarPath("build/libs/shadow-1.0.jar").use { it.manifest } + assertThat(mf).isNotNull() + assertThat(mf.mainAttributes.getValue("Test-Entry")).isEqualTo("FAILED") + assertThat(mf.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") + assertThat(mf.mainAttributes.getValue("New-Entry")).isNull() + } + + @Test + fun canUseCustomTransformer() { + writeMainClass() + projectScriptPath.appendText( + """ + $shadowJar { + // Use NoOpTransformer to mock a custom transformer here. + transform(${NoOpTransformer::class.java.name}.INSTANCE) + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).isRegular() } @ParameterizedTest @@ -134,6 +144,18 @@ class TransformersTest : BaseTransformerTest() { assertThat(outputShadowJar).isRegular() } + private fun commonAssertions( + mainAttributesBlock: Attributes.() -> Unit = { + assertThat(getValue("Test-Entry")).isEqualTo("PASSED") + assertThat(getValue("Main-Class")).isEqualTo("shadow.Main") + assertThat(getValue("New-Entry")).isEqualTo("NEW") + }, + ) { + val mf = outputShadowJar.use { it.manifest } + assertThat(mf).isNotNull() + mainAttributesBlock(mf.mainAttributes) + } + private companion object { val MANIFEST_ATTRS = """ jar { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt similarity index 96% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index 5786ec8bd..a9764473b 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -20,7 +20,7 @@ class AppendableMavenRepository( private val modules = mutableListOf() init { - root.createDirectories() + root.resolve("temp").createDirectories() root.resolve("settings.gradle").createFile() .writeText("rootProject.name = '${root.name}'") projectBuildScript = root.resolve("build.gradle").createFile() @@ -109,7 +109,7 @@ class AppendableMavenRepository( fun buildJar(builder: JarBuilder.() -> Unit) { val jarName = coordinate.replace(":", "-") + ".jar" - existingJar = JarBuilder(root.resolve(jarName)).apply(builder).write() + existingJar = JarBuilder(root.resolve("temp/$jarName")).apply(builder).write() } fun addDependency(groupId: String, artifactId: String, version: String, scope: String = "runtime") { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt similarity index 87% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt index d59675a6c..6da7edaf8 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt @@ -12,8 +12,8 @@ class JarBuilder( private val entries = mutableSetOf() private val jos = JarOutputStream(outputPath.outputStream()) - fun insert(path: String, content: String): JarBuilder = apply { - contents[path] = content + fun insert(entry: String, content: String): JarBuilder = apply { + contents[entry] = content } fun write(): Path { @@ -25,7 +25,7 @@ class JarBuilder( } if (entries.add(entry)) { jos.putNextEntry(JarEntry(entry)) - content.byteInputStream().copyTo(jos) + content.byteInputStream().use { it.copyTo(jos) } } } } diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt similarity index 66% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt rename to src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index d7f7b3c9f..9e10f3395 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -6,7 +6,6 @@ import assertk.assertions.isNotEmpty import assertk.fail import java.nio.file.Path import java.util.jar.JarFile -import kotlin.io.path.deleteExisting /** * A wrapper for [JarFile] that also implements [Path]. @@ -18,9 +17,8 @@ class JarPath(val path: Path) : JarFile(path.toFile()), Path by path { - fun deleteExisting() { - close() - path.deleteExisting() + fun getMainAttr(name: String): String? { + return manifest.mainAttributes.getValue(name) } fun getContent(entryName: String): String { @@ -29,23 +27,26 @@ class JarPath(val path: Path) : } } -fun Assert.useAll(body: Assert.() -> Unit) = all { - body() - given { it.close() } -} - /** * Common regular assertions for [JarPath]. */ -fun Assert.isRegular() = transform { it.entries().toList() }.isNotEmpty() +fun Assert.isRegular() = all { + transform { it.entries().toList() }.isNotEmpty() + // Close the resource after all assertions are done. + given { it.use(block = {}) } +} + +fun Assert.getContent(entryName: String) = transform { it.getContent(entryName) } + +fun Assert.getMainAttr(name: String) = transform { it.getMainAttr(name) } -fun Assert.containsEntries(vararg entries: String) = transform { actual -> +fun Assert.containsEntries(vararg entries: String) = given { actual -> entries.forEach { entry -> actual.getEntry(entry) ?: fail("Jar file ${actual.path} does not contain entry $entry") } } -fun Assert.doesNotContainEntries(vararg entries: String) = transform { actual -> +fun Assert.doesNotContainEntries(vararg entries: String) = given { actual -> entries.forEach { entry -> actual.getEntry(entry) ?: return@forEach fail("Jar file ${actual.path} contains entry $entry") diff --git a/src/intiTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/src/funcTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension similarity index 100% rename from src/intiTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension rename to src/funcTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheTest.kt deleted file mode 100644 index 29539c00f..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ConfigurationCacheTest.kt +++ /dev/null @@ -1,140 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.isEqualTo -import assertk.assertions.isNotNull -import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries -import kotlin.io.path.appendText -import kotlin.io.path.writeText -import org.gradle.testkit.runner.BuildResult -import org.gradle.testkit.runner.TaskOutcome -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -class ConfigurationCacheTest : BasePluginTest() { - @BeforeEach - override fun setup() { - super.setup() - projectScriptPath.appendText( - """ - dependencies { - implementation 'shadow:a:1.0' - implementation 'shadow:b:1.0' - } - """.trimIndent() + System.lineSeparator(), - ) - } - - @Test - fun supportsConfigurationCache() { - writeMainClass() - - projectScriptPath.appendText( - """ - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - $runShadow { - args 'foo' - } - """.trimIndent(), - ) - - run(shadowJarTask) - val result = run(shadowJarTask) - - result.assertCcReused() - } - - @Test - fun configurationCachingSupportsExcludes() { - projectScriptPath.appendText( - """ - $shadowJar { - exclude 'a2.properties' - } - """.trimIndent(), - ) - - run(shadowJarTask) - outputShadowJar.deleteExisting() - val result = run(shadowJarTask) - - assertThat(outputShadowJar).containsEntries( - "a.properties", - "b.properties", - ) - assertThat(outputShadowJar).doesNotContainEntries( - "a2.properties", - ) - result.assertCcReused() - } - - @Test - fun configurationCachingSupportsMinimize() { - writeClientAndServerModules( - serverShadowBlock = """ - minimize { - exclude(dependency('junit:junit:.*')) - } - """.trimIndent(), - ) - - run(serverShadowJarTask) - outputServerShadowJar.deleteExisting() - val result = run(serverShadowJarTask) - - assertThat(outputServerShadowJar).containsEntries( - "server/Server.class", - "junit/framework/Test.class", - ) - assertThat(outputServerShadowJar).doesNotContainEntries( - "client/Client.class", - ) - result.assertCcReused() - } - - @Test - fun configurationCachingOfConfigurationsIsUpToDate() { - settingsScriptPath.appendText( - """ - include 'lib' - """.trimIndent(), - ) - - path("lib/src/main/java/lib/Lib.java").writeText( - """ - package lib; - public class Lib {} - """.trimIndent(), - ) - path("lib/build.gradle").writeText( - """ - ${getDefaultProjectBuildScript()} - dependencies { - implementation 'junit:junit:3.8.2' - } - $shadowJar { - configurations = [project.configurations.compileClasspath] - } - """.trimIndent(), - ) - - val libShadowJarTask = ":lib:$SHADOW_JAR_TASK_NAME" - run(libShadowJarTask) - val result = run(libShadowJarTask) - - assertThat(result.task(libShadowJarTask)).isNotNull() - .transform { it.outcome }.isEqualTo(TaskOutcome.UP_TO_DATE) - result.assertCcReused() - } - - private fun BuildResult.assertCcReused() { - assertThat(output).contains("Reusing configuration cache.") - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt similarity index 67% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt index 887c4a273..76363f9c4 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/DocCodeSnippetTest.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt @@ -1,9 +1,9 @@ -package com.github.jengelman.gradle.plugins.shadow.doc +package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.doc.executable.CodeSnippetExtractor -import com.github.jengelman.gradle.plugins.shadow.doc.executor.GroovyBuildExecutor -import com.github.jengelman.gradle.plugins.shadow.doc.executor.NoopExecutor -import com.github.jengelman.gradle.plugins.shadow.doc.fixture.GroovyDslFixture +import com.github.jengelman.gradle.plugins.shadow.executable.CodeSnippetExtractor +import com.github.jengelman.gradle.plugins.shadow.executor.GroovyBuildExecutor +import com.github.jengelman.gradle.plugins.shadow.executor.NoopExecutor +import com.github.jengelman.gradle.plugins.shadow.fixture.GroovyDslFixture import java.nio.file.Path import kotlin.io.path.Path import org.junit.jupiter.api.DynamicTest diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt deleted file mode 100644 index b12cf69fa..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isNotNull -import com.github.jengelman.gradle.plugins.shadow.BasePluginTest -import java.nio.file.Path -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.appendText -import kotlin.io.path.copyToRecursively -import kotlin.io.path.deleteRecursively -import kotlin.io.path.listDirectoryEntries -import org.gradle.testkit.runner.GradleRunner -import org.gradle.testkit.runner.TaskOutcome -import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE -import org.gradle.testkit.runner.TaskOutcome.SUCCESS -import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.io.TempDir - -abstract class BaseCachingTest : BasePluginTest() { - - @TempDir - lateinit var alternateDir: Path - - @BeforeEach - override fun setup() { - super.setup() - // Use a test-specific build cache directory. This ensures that we'll only use cached outputs generated during - // this test, and we won't accidentally use cached outputs from a different test or a different build. - settingsScriptPath.appendText( - """ - buildCache { - local { - directory = file('build-cache') - } - } - """.trimIndent() + System.lineSeparator(), - ) - } - - @OptIn(ExperimentalPathApi::class) - fun assertShadowJarIsCachedAndRelocatable( - firstOutcome: TaskOutcome = FROM_CACHE, - secondOutcome: TaskOutcome = UP_TO_DATE, - ) { - try { - outputShadowJar.deleteExisting() - } catch (ignored: IllegalStateException) { - // ignore if the file does not exist - } - alternateDir.deleteRecursively() - root.copyToRecursively(alternateDir, followLinks = false, overwrite = false) - // check that shadowJar pulls from cache in the original directory - assertShadowJarHasResult(firstOutcome) - // check that shadowJar pulls from cache in a different directory - assertShadowJarHasResult(secondOutcome) { - if (alternateDir.listDirectoryEntries().isEmpty()) { - error("Directory was not copied to alternate directory") - } - it.withProjectDir(alternateDir.toFile()) - } - } - - fun assertShadowJarExecutes() { - try { - outputShadowJar.deleteExisting() - } catch (ignored: IllegalStateException) { - // ignore if the file does not exist - } - // task was executed and not pulled from cache - assertShadowJarHasResult(SUCCESS) - } - - private fun assertShadowJarHasResult( - expectedOutcome: TaskOutcome, - runnerBlock: (GradleRunner) -> GradleRunner = { it }, - ) { - val result = run("--build-cache", shadowJarTask, runnerBlock = runnerBlock) - assertThat(result.task(shadowJarTask)).isNotNull() - .transform { it.outcome }.isEqualTo(expectedOutcome) - } -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt deleted file mode 100644 index 09f87c315..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/SnippetExecutor.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.doc.executor - -import com.github.jengelman.gradle.plugins.shadow.doc.fixture.SnippetFixture -import java.nio.file.Path - -interface SnippetExecutor { - val fixture: SnippetFixture - - fun execute(tempDir: Path, snippet: String) -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt deleted file mode 100644 index 8a20af065..000000000 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/SnippetFixture.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.doc.fixture - -interface SnippetFixture { - val pre: String -} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt similarity index 81% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt index 7a734b6b3..0eaf65ad6 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExecutable.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt @@ -1,6 +1,6 @@ -package com.github.jengelman.gradle.plugins.shadow.doc.executable +package com.github.jengelman.gradle.plugins.shadow.executable -import com.github.jengelman.gradle.plugins.shadow.doc.executor.SnippetExecutor +import com.github.jengelman.gradle.plugins.shadow.executor.SnippetExecutor import java.nio.file.Path import kotlin.io.path.createTempDirectory import org.junit.jupiter.api.function.Executable diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt similarity index 92% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt index 334cdee26..650fb9554 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executable/CodeSnippetExtractor.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt @@ -1,7 +1,7 @@ -package com.github.jengelman.gradle.plugins.shadow.doc.executable +package com.github.jengelman.gradle.plugins.shadow.executable -import com.github.jengelman.gradle.plugins.shadow.doc.DocCodeSnippetTest -import com.github.jengelman.gradle.plugins.shadow.doc.executor.SnippetExecutor +import com.github.jengelman.gradle.plugins.shadow.DocCodeSnippetTest +import com.github.jengelman.gradle.plugins.shadow.executor.SnippetExecutor import java.nio.file.Path import java.util.regex.Pattern import kotlin.io.path.ExperimentalPathApi diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt similarity index 92% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt index 556f93fb2..fbe45440d 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/GroovyBuildExecutor.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt @@ -1,6 +1,6 @@ -package com.github.jengelman.gradle.plugins.shadow.doc.executor +package com.github.jengelman.gradle.plugins.shadow.executor -import com.github.jengelman.gradle.plugins.shadow.doc.fixture.SnippetFixture +import com.github.jengelman.gradle.plugins.shadow.fixture.SnippetFixture import java.nio.file.Path import kotlin.io.path.createDirectory import kotlin.io.path.writeText diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt similarity index 61% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt index 3820b8aef..1123baddb 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/executor/NoopExecutor.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt @@ -1,6 +1,6 @@ -package com.github.jengelman.gradle.plugins.shadow.doc.executor +package com.github.jengelman.gradle.plugins.shadow.executor -import com.github.jengelman.gradle.plugins.shadow.doc.fixture.SnippetFixture +import com.github.jengelman.gradle.plugins.shadow.fixture.SnippetFixture import java.nio.file.Path object NoopExecutor : SnippetExecutor { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt new file mode 100644 index 000000000..0b445e007 --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt @@ -0,0 +1,10 @@ +package com.github.jengelman.gradle.plugins.shadow.executor + +import com.github.jengelman.gradle.plugins.shadow.fixture.SnippetFixture +import java.nio.file.Path + +interface SnippetExecutor { + val fixture: SnippetFixture + + fun execute(tempDir: Path, snippet: String) +} diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt similarity index 90% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt rename to src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt index e7dacf74d..525dbce85 100644 --- a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/doc/fixture/GroovyDslFixture.kt +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt @@ -1,4 +1,4 @@ -package com.github.jengelman.gradle.plugins.shadow.doc.fixture +package com.github.jengelman.gradle.plugins.shadow.fixture object GroovyDslFixture : SnippetFixture { diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt new file mode 100644 index 000000000..95a2ebf5d --- /dev/null +++ b/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt @@ -0,0 +1,5 @@ +package com.github.jengelman.gradle.plugins.shadow.fixture + +interface SnippetFixture { + val pre: String +} diff --git a/src/intiTest/resources/junit-3.8.2.jar b/src/intiTest/resources/junit-3.8.2.jar deleted file mode 100644 index c8f711d050eff209321f799d85ebb3bbe305d481..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 120640 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
4LLYqQ5{NoW8NO+g^8MA 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 A>i_@% diff --git a/src/intiTest/resources/test-artifact-1.0-SNAPSHOT.jar b/src/intiTest/resources/test-artifact-1.0-SNAPSHOT.jar deleted file mode 100644 index 009abad49de5cbf4857e8c429cb1f81c75c2dd56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3115 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 ZiPmb76=Ewli&KF45oX>c2|m`p{{iiGhC2WN diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index cdc290c45..0e0fec3c7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -63,10 +63,15 @@ public abstract class ShadowApplicationPlugin : Plugin { protected open fun addCreateScriptsTask() { project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { + val unixStartScript = + requireResourceAsText("com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt") + val windowsStartScript = + requireResourceAsText("com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt") + (it.unixStartScriptGenerator as TemplateBasedScriptGenerator).template = - project.resources.text.fromString(this::class.java.requireResourceAsText("internal/unixStartScript.txt")) + project.resources.text.fromString(unixStartScript) (it.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = - project.resources.text.fromString(this::class.java.requireResourceAsText("internal/windowsStartScript.txt")) + project.resources.text.fromString(windowsStartScript) it.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" it.group = ApplicationPlugin.APPLICATION_GROUP it.classpath = project.files(shadowJar) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index daac5eae8..97557c31c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -3,9 +3,9 @@ package com.github.jengelman.gradle.plugins.shadow.internal import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.File -import java.io.FileNotFoundException import java.io.InputStream import java.nio.charset.Charset +import java.nio.file.NoSuchFileException import java.util.Properties import org.gradle.api.file.RelativePath import org.gradle.api.internal.file.DefaultFileTreeElement @@ -37,12 +37,13 @@ internal fun Properties.inputStream( return os.toByteArray().inputStream() } -internal fun Class<*>.requireResourceAsText(name: String): String { - return requireResourceAsStream(name).bufferedReader().readText() +internal fun requireResourceAsText(name: String): String { + return requireResourceAsStream(name).bufferedReader().use { it.readText() } } -private fun Class<*>.requireResourceAsStream(name: String): InputStream { - return getResourceAsStream(name) ?: throw FileNotFoundException("Resource $name not found.") +internal fun requireResourceAsStream(name: String): InputStream { + return Utils::class.java.classLoader.getResourceAsStream(name) + ?: throw NoSuchFileException("Resource $name not found.") } private val DummyFile = File("dummy") @@ -51,3 +52,5 @@ private val DummyStat = object : Stat { override fun getUnixMode(f: File): Int = error("This is a dummy implementation.") override fun stat(f: File): FileMetadata = error("This is a dummy implementation.") } + +private object Utils diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt index fdffe3788..1c5314bba 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt @@ -12,7 +12,7 @@ public abstract class KnowsTask : DefaultTask() { """ No, The Shadow Knows.... - ${this::class.java.requireResourceAsText("/shadowBanner.txt")} + ${requireResourceAsText("shadowBanner.txt")} """.trimIndent(), ) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 77fe69a6c..2ef97d17a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -61,6 +61,7 @@ public abstract class ShadowJar : manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { + // TODO: this has been called but the cache is still working fine, need to investigate why. transformers.get().any { !isCacheableTransform(it::class.java) } || relocators.get().any { !isCacheableRelocator(it::class.java) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index 029d2e5b8..182d12f33 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -53,6 +53,7 @@ public open class AppendingTransformer @Inject constructor( entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) + // Closing a ByteArrayOutputStream has no effect, so we don't use a use block here. data.toByteArray().inputStream().copyTo(os) data.reset() } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 2950715ae..27560fd88 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -169,7 +169,7 @@ public open class PropertiesFileTransformer @Inject constructor( private fun loadAndTransformKeys(inputStream: InputStream): CleanProperties { val props = CleanProperties() // InputStream closed by caller, so we don't do it here. - props.load(inputStream.reader(charset)) + props.load(inputStream.bufferedReader(charset)) return transformKeys(props) } @@ -225,7 +225,7 @@ public open class PropertiesFileTransformer @Inject constructor( val entry = ZipEntry(path) entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) - props.inputStream(charset).reader(charset).use { + props.inputStream(charset).bufferedReader(charset).use { it.copyTo(zipWriter) } zipWriter.flush() diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 18b0c4dbc..6e94fcf97 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -2,10 +2,9 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory -import java.io.FileNotFoundException -import java.io.InputStream import java.lang.reflect.ParameterizedType import java.nio.file.Path import java.util.Locale @@ -23,10 +22,6 @@ abstract class BaseTransformerTest { protected val manifestTransformerContext: TransformerContext get() = TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME)) - protected fun requireResourceAsStream(name: String): InputStream { - return this::class.java.classLoader.getResourceAsStream(name) ?: throw FileNotFoundException("Resource $name not found.") - } - @BeforeEach fun setup() { @Suppress("UNCHECKED_CAST") @@ -66,6 +61,7 @@ abstract class BaseTransformerTest { * choice to test for improper case-less string comparisons. */ fun setupTurkishLocale() { + @Suppress("DEPRECATION") Locale.setDefault(Locale("tr")) } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt index 43640d449..73b68d961 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream import org.custommonkey.xmlunit.XMLUnit import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index f411b33a4..945b9bc8d 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -3,10 +3,11 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import com.github.jengelman.gradle.plugins.shadow.util.SimpleRelocator import java.io.File -import java.net.URL +import java.net.URI import java.util.Collections import org.apache.logging.log4j.core.config.plugins.processor.PluginCache import org.apache.tools.zip.ZipOutputStream @@ -39,8 +40,9 @@ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest() { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 55bec8828..debd4001e 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -28,8 +28,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() } assertThat(transformer.hasTransformedResource()).isTrue() - assertThat(transformer.serviceEntries.getValue(path).toInputStream().bufferedReader().readText()) - .isEqualTo(output) + val entry = transformer.serviceEntries.getValue(path).toInputStream().bufferedReader().use { it.readText() } + assertThat(entry).isEqualTo(output) } @Test From eba1c30a5f22dd1dcf39cc4138371016736f25c6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 10 Jan 2025 22:01:08 -0500 Subject: [PATCH 133/941] Note CHANGELOG in README (#1142) --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b9c94e745..b9d8266c9 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,8 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. ## Documentation -Read the [User Guide](https://gradleup.com/shadow/)! +- [User Guide](https://gradleup.com/shadow/) +- [CHANGELOG](src/docs/changes/README.md) ## Current Status From f94417c263db96b18fe978573adcb7db39474a68 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 11 Jan 2025 03:16:59 -0500 Subject: [PATCH 134/941] Migrate to JVM Test Suite (#1141) https://docs.gradle.org/current/userguide/jvm_test_suite_plugin.html https://blog.gradle.org/introducing-test-suites --- build.gradle.kts | 134 +++++++++--------- gradle/libs.versions.toml | 2 - .../gradle/plugins/shadow/ApplicationTest.kt | 0 .../gradle/plugins/shadow/BasePluginTest.kt | 0 .../gradle/plugins/shadow/FilteringTest.kt | 0 .../gradle/plugins/shadow/PublishingTest.kt | 0 .../gradle/plugins/shadow/RelocationTest.kt | 0 .../gradle/plugins/shadow/ShadowPluginTest.kt | 0 .../plugins/shadow/caching/BaseCachingTest.kt | 0 .../shadow/caching/MinimizationCachingTest.kt | 0 .../shadow/caching/RelocationCachingTest.kt | 0 .../shadow/caching/ShadowJarCachingTest.kt | 0 .../shadow/caching/TransformCachingTest.kt | 0 .../transformers/AppendingTransformerTest.kt | 0 .../transformers/BaseTransformerTest.kt | 0 .../GroovyExtensionModuleTransformerTest.kt | 0 .../ServiceFileTransformerTest.kt | 0 .../shadow/transformers/TransformersTest.kt | 0 .../shadow/util/AppendableMavenRepository.kt | 0 .../shadow/util/GradleModuleMetadata.kt | 0 .../gradle/plugins/shadow/util/Issue.kt | 0 .../gradle/plugins/shadow/util/JarBuilder.kt | 0 .../gradle/plugins/shadow/util/JarPath.kt | 0 .../org.junit.jupiter.api.extension.Extension | 0 .../resources/junit-3.8.2.jar | Bin .../resources/test-artifact-1.0-SNAPSHOT.jar | Bin .../resources/test-project-1.0-SNAPSHOT.jar | Bin .../plugins/shadow/DocCodeSnippetTest.kt | 0 .../executable/CodeSnippetExecutable.kt | 0 .../shadow/executable/CodeSnippetExtractor.kt | 0 .../shadow/executor/GroovyBuildExecutor.kt | 0 .../plugins/shadow/executor/NoopExecutor.kt | 0 .../shadow/executor/SnippetExecutor.kt | 0 .../shadow/fixture/GroovyDslFixture.kt | 0 .../plugins/shadow/fixture/SnippetFixture.kt | 0 35 files changed, 68 insertions(+), 68 deletions(-) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt (100%) rename src/{funcTest => functionalTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt (100%) rename src/{funcTest => functionalTest}/resources/META-INF/services/org.junit.jupiter.api.extension.Extension (100%) rename src/{funcTest => functionalTest}/resources/junit-3.8.2.jar (100%) rename src/{funcTest => functionalTest}/resources/test-artifact-1.0-SNAPSHOT.jar (100%) rename src/{funcTest => functionalTest}/resources/test-project-1.0-SNAPSHOT.jar (100%) rename src/{intiTest => integrationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt (100%) rename src/{intiTest => integrationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt (100%) rename src/{intiTest => integrationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt (100%) rename src/{intiTest => integrationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt (100%) rename src/{intiTest => integrationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt (100%) rename src/{intiTest => integrationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt (100%) rename src/{intiTest => integrationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt (100%) rename src/{intiTest => integrationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 03f2e1838..9fa451fde 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,5 @@ +@file:Suppress("UnstableApiUsage") + import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion @@ -44,26 +46,6 @@ spotless { } } -val intiTest: SourceSet by sourceSets.creating -val intiTestImplementation: Configuration by configurations.getting { - extendsFrom(configurations.testImplementation.get()) -} -val intiTestRuntimeOnly: Configuration by configurations.getting { - extendsFrom(configurations.testRuntimeOnly.get()) -} - -val funcTest: SourceSet by sourceSets.creating -val funcTestImplementation: Configuration by configurations.getting { - extendsFrom(configurations.testImplementation.get()) -} -val funcTestRuntimeOnly: Configuration by configurations.getting { - extendsFrom(configurations.testRuntimeOnly.get()) -} - -gradlePlugin { - testSourceSets.add(funcTest) -} - dependencies { implementation(libs.apache.ant) implementation(libs.apache.commonsIo) @@ -74,61 +56,81 @@ dependencies { implementation(libs.plexus.utils) implementation(libs.plexus.xml) - testImplementation(platform(libs.junit.bom)) - testImplementation(libs.junit.jupiter) - testImplementation(libs.assertk) - testImplementation(libs.xmlunit) - testRuntimeOnly(libs.junit.platformLauncher) - - funcTestImplementation(sourceSets.main.get().output) - funcTestImplementation(libs.apache.maven.modelBuilder) - funcTestImplementation(libs.moshi) - funcTestImplementation(libs.moshi.kotlin) - lintChecks(libs.androidx.gradlePluginLints) } -val integrationTest by tasks.registering(Test::class) { - description = "Runs the integration tests." - group = LifecycleBasePlugin.VERIFICATION_GROUP - testClassesDirs = intiTest.output.classesDirs - classpath = intiTest.runtimeClasspath +testing.suites { + getByName("test") { + dependencies { + implementation(libs.xmlunit) + } + } + register("integrationTest") { + testType = TestSuiteType.INTEGRATION_TEST + targets.configureEach { + testTask { + val docsDir = file("src/docs") + // Add src/docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. + inputs.dir(docsDir) + systemProperty("DOCS_DIR", docsDir.absolutePath) + } + } + } + register("functionalTest") { + testType = TestSuiteType.FUNCTIONAL_TEST + targets.configureEach { + testTask { + // Required to enable `IssueExtension` for all tests. + systemProperty("junit.jupiter.extensions.autodetection.enabled", true) + + // Required to test configuration cache in tests when using withDebug() + // https://github.com/gradle/gradle/issues/22765#issuecomment-1339427241 + jvmArgs( + "--add-opens", + "java.base/java.util=ALL-UNNAMED", + "--add-opens", + "java.base/java.util.concurrent.atomic=ALL-UNNAMED", + "--add-opens", + "java.base/java.lang.invoke=ALL-UNNAMED", + "--add-opens", + "java.base/java.net=ALL-UNNAMED", + ) + } + } + dependencies { + // Seems we can't ref project() here due to some limitations of rootProject. + implementation(sourceSets.main.get().output) + implementation(libs.apache.ant) + implementation(libs.apache.maven.modelBuilder) + implementation(libs.moshi) + implementation(libs.moshi.kotlin) + } + } - val docsDir = file("src/docs") - // Add src/docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. - inputs.dir(docsDir) - systemProperty("DOCS_DIR", docsDir.absolutePath) + withType().configureEach { + useJUnitJupiter(libs.junit.bom.map { requireNotNull(it.version) }) + dependencies { + implementation(libs.assertk) + } + targets.configureEach { + testTask { + maxParallelForks = Runtime.getRuntime().availableProcessors() + } + } + } } -val functionalTest by tasks.registering(Test::class) { - description = "Runs the functional tests." - group = LifecycleBasePlugin.VERIFICATION_GROUP - testClassesDirs = funcTest.output.classesDirs - classpath = funcTest.runtimeClasspath - - // Required to enable `IssueExtension` for all tests. - systemProperty("junit.jupiter.extensions.autodetection.enabled", true) +gradlePlugin { + testSourceSets( + sourceSets["functionalTest"], + sourceSets["integrationTest"], + ) } tasks.check { - dependsOn(integrationTest, functionalTest) -} - -tasks.withType().configureEach { - useJUnitPlatform() - maxParallelForks = Runtime.getRuntime().availableProcessors() - - // Required to test configuration cache in tests when using withDebug() - // https://github.com/gradle/gradle/issues/22765#issuecomment-1339427241 - jvmArgs( - "--add-opens", - "java.base/java.util=ALL-UNNAMED", - "--add-opens", - "java.base/java.util.concurrent.atomic=ALL-UNNAMED", - "--add-opens", - "java.base/java.lang.invoke=ALL-UNNAMED", - "--add-opens", - "java.base/java.net=ALL-UNNAMED", + dependsOn( + testing.suites.named("integrationTest"), + testing.suites.named("functionalTest"), ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 478001332..c4b8bdbfe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,8 +26,6 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha03" ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" junit-bom = "org.junit:junit-bom:5.11.4" -junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" } -junit-platformLauncher = { module = "org.junit.platform:junit-platform-launcher" } assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt diff --git a/src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt similarity index 100% rename from src/funcTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt diff --git a/src/funcTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/src/functionalTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension similarity index 100% rename from src/funcTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension rename to src/functionalTest/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/src/funcTest/resources/junit-3.8.2.jar b/src/functionalTest/resources/junit-3.8.2.jar similarity index 100% rename from src/funcTest/resources/junit-3.8.2.jar rename to src/functionalTest/resources/junit-3.8.2.jar diff --git a/src/funcTest/resources/test-artifact-1.0-SNAPSHOT.jar b/src/functionalTest/resources/test-artifact-1.0-SNAPSHOT.jar similarity index 100% rename from src/funcTest/resources/test-artifact-1.0-SNAPSHOT.jar rename to src/functionalTest/resources/test-artifact-1.0-SNAPSHOT.jar diff --git a/src/funcTest/resources/test-project-1.0-SNAPSHOT.jar b/src/functionalTest/resources/test-project-1.0-SNAPSHOT.jar similarity index 100% rename from src/funcTest/resources/test-project-1.0-SNAPSHOT.jar rename to src/functionalTest/resources/test-project-1.0-SNAPSHOT.jar diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt rename to src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt rename to src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt rename to src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt rename to src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt rename to src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt rename to src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt rename to src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt diff --git a/src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt similarity index 100% rename from src/intiTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt rename to src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt From 1fb1eafb0e12d723cd0d40a5e8d354fdc7c292d2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 11 Jan 2025 10:23:51 -0500 Subject: [PATCH 135/941] Tweak the doc about packaging Gradle plugins (#1143) --- src/docs/plugins/README.md | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/docs/plugins/README.md b/src/docs/plugins/README.md index b4b6a4840..e65c9534e 100644 --- a/src/docs/plugins/README.md +++ b/src/docs/plugins/README.md @@ -11,10 +11,10 @@ and `relocationPrefix` settings on any `ShadowJar` task. A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the `shadowJar` task for relocation. ```groovy -dependencies { - shadow localGroovy() - shadow gradleApi() +apply plugin: 'java-gradle-plugin' +apply plugin: 'com.gradleup.shadow' +dependencies { implementation 'org.jdom:jdom2:2.0.6' implementation 'org.ow2.asm:asm:6.0' implementation 'org.ow2.asm:asm-commons:6.0' @@ -25,14 +25,10 @@ dependencies { tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { enableRelocation = true + archiveClassifier = '' } ``` -Note that the `localGroovy()` and `gradleApi()` dependencies are added to the `shadow` configuration instead of the -normal `compile` configuration. These 2 dependencies are provided by Gradle to compile your project but are ultimately -provided by the Gradle runtime when executing the plugin. Thus, it is **not** advisable to bundle these dependencies -with your plugin. - ## Publishing shadowed Gradle plugins The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. Starting with this version, plugin projects that apply both Shadow and the Gradle Plugin Publish plugin will be From cf638bd4700de7b8fb79f378d3b655b8a91bc58b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 11 Jan 2025 11:01:51 -0500 Subject: [PATCH 136/941] Eliminate TODO in doc snippet tests (#1144) --- .../gradle/plugins/shadow/DocCodeSnippetTest.kt | 4 ++-- .../shadow/executable/CodeSnippetExecutable.kt | 5 ++--- .../shadow/executable/CodeSnippetExtractor.kt | 12 ++++++------ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt index 76363f9c4..68b2a0e93 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt @@ -13,9 +13,9 @@ import org.junit.jupiter.api.io.TempDir class DocCodeSnippetTest { @TestFactory - fun provideDynamicTests(@TempDir tempDir: Path): List { + fun provideDynamicTests(@TempDir root: Path): List { return fixtures.flatMap { (selector, executor) -> - CodeSnippetExtractor.extract(tempDir, docsDir, selector, executor) + CodeSnippetExtractor.extract(root, docsDir, selector, executor) }.map { DynamicTest.dynamicTest(it.testName, it) } diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt index 0eaf65ad6..d2b80e573 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt @@ -6,7 +6,7 @@ import kotlin.io.path.createTempDirectory import org.junit.jupiter.api.function.Executable class CodeSnippetExecutable( - private val tempDir: Path, + private val root: Path, private val snippet: String, val testName: String, private val executor: SnippetExecutor, @@ -14,8 +14,7 @@ class CodeSnippetExecutable( ) : Executable { override fun execute() { try { - // TODO: any way to createTempDirectory with `@TempDir` for each `Executable`? - executor.execute(createTempDirectory(tempDir, "doc-"), snippet) + executor.execute(createTempDirectory(root), snippet) } catch (t: Throwable) { throw exceptionTransformer(t) } diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt index 650fb9554..95c5f5369 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt @@ -13,7 +13,7 @@ import kotlin.io.path.walk object CodeSnippetExtractor { fun extract( - tempDir: Path, + root: Path, docRoot: Path, cssClass: String, executor: SnippetExecutor, @@ -24,13 +24,13 @@ object CodeSnippetExtractor { docRoot.walk() .filter { it.name.endsWith(".md", ignoreCase = true) } .forEach { - addSnippets(tempDir, snippets, it, snippetBlockPattern, executor) + addSnippets(root, snippets, it, snippetBlockPattern, executor) } return snippets } private fun addSnippets( - tempDir: Path, + root: Path, snippets: MutableList, path: Path, snippetBlockPattern: Pattern, @@ -41,7 +41,7 @@ object CodeSnippetExtractor { val snippetsByLine = findSnippetsByLine(source, snippetBlockPattern) snippetsByLine.forEach { (lineNumber, snippet) -> - snippets.add(createSnippet(tempDir, relativeDocPath, path, lineNumber, snippet, executor)) + snippets.add(createSnippet(root, relativeDocPath, path, lineNumber, snippet, executor)) } } @@ -72,7 +72,7 @@ object CodeSnippetExtractor { } private fun createSnippet( - tempDir: Path, + root: Path, sourceClassName: String, sourcePath: Path, lineNumber: Int, @@ -80,7 +80,7 @@ object CodeSnippetExtractor { executor: SnippetExecutor, ): CodeSnippetExecutable { return CodeSnippetExecutable( - tempDir, + root, snippet, "$sourceClassName:$lineNumber", executor, From fb0d7a292f78ae2f14e6539f45059340545a0da5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 12 Jan 2025 00:06:18 +0800 Subject: [PATCH 137/941] Migrate config .github/renovate.json5 (#1145) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/renovate.json5 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index d09a6b53f..e3eb18c10 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -1,6 +1,6 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base", - ] + $schema: 'https://docs.renovatebot.com/renovate-schema.json', + extends: [ + 'config:recommended', + ], } From 770109a7d216a24d65086c522b3b6ae33994afbc Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 11 Jan 2025 11:50:25 -0500 Subject: [PATCH 138/941] Keep the original IOException handling in ShadowCopyAction.execute (#1147) --- .../gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index b16de7ec8..82e09ad13 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -118,10 +118,12 @@ public open class ShadowCopyAction internal constructor( processTransformers(outputStream) } } catch (e: IOException) { - throw Zip64RequiredException( - "${e.cause?.message}\n\nTo build this archive, please enable the zip64 extension.\n" + - "See: ${documentationRegistry.getDslRefForProperty(Zip::class.java, "zip64")}", - ) + if (e.cause is Zip64RequiredException) { + throw Zip64RequiredException( + "${e.cause?.message}\n\nTo build this archive, please enable the zip64 extension.\n" + + "See: ${documentationRegistry.getDslRefForProperty(Zip::class.java, "zip64")}", + ) + } } return WorkResults.didWork(true) } From a671b7133e30e9d77f19cfbe54e019da0b242dc7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 11 Jan 2025 20:30:12 -0500 Subject: [PATCH 139/941] Fail builds if processing bad jars (#1146) Co-authored-by: Carlos Lopez --- src/docs/changes/README.md | 4 +++ .../gradle/plugins/shadow/ShadowPluginTest.kt | 29 +++++++++++++++++++ .../plugins/shadow/tasks/ShadowCopyAction.kt | 2 ++ 3 files changed, 35 insertions(+) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index fbec6de81..bf38070dd 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -13,6 +13,10 @@ Some public `List` parameters are also changed to `Set`. - Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) +**Fixed** + +- Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) + ## [v9.0.0-beta4] (2024-12-06) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt index 46570901d..5283dfd58 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow +import assertk.all import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEqualTo @@ -18,6 +19,7 @@ import kotlin.io.path.readText import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin import org.gradle.testfixtures.ProjectBuilder +import org.gradle.testkit.runner.TaskOutcome.FAILED import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.junit.jupiter.api.Disabled @@ -830,6 +832,33 @@ class ShadowPluginTest : BasePluginTest() { assertThat(result.output).contains("Reusing configuration cache.") } + @Issue( + "https://github.com/GradleUp/shadow/issues/915", + ) + @Test + fun failBuildIfProcessingBadJar() { + val badJarPath = path("bad.jar").apply { + writeText("A bad jar.") + }.toUri().toURL().path + + projectScriptPath.appendText( + """ + dependencies { + implementation files('$badJarPath') + } + """.trimIndent(), + ) + + val result = runWithFailure(shadowJarTask) + + assertThat(result).all { + taskOutcomeEquals(shadowJarTask, FAILED) + transform { it.output }.contains( + "java.util.zip.ZipException: archive is not a ZIP archive", + ) + } + } + private fun writeShadowedClientAndServerModules() { writeClientAndServerModules() path("client/build.gradle").appendText( diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 82e09ad13..f9eb496b4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -124,6 +124,8 @@ public open class ShadowCopyAction internal constructor( "See: ${documentationRegistry.getDslRefForProperty(Zip::class.java, "zip64")}", ) } + // Rethrow the exception like `java.util.zip.ZipException: archive is not a ZIP archive`. + throw e } return WorkResults.didWork(true) } From bd25e8dc81d39deae1fe658e572fedce19a40d45 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 12 Jan 2025 01:24:30 -0500 Subject: [PATCH 140/941] Add a test for using archiveFileName (#1148) --- .../gradle/plugins/shadow/ShadowPluginTest.kt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt index 5283dfd58..91f9d778d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -859,6 +859,30 @@ class ShadowPluginTest : BasePluginTest() { } } + @Test + fun worksWithArchiveFileName() { + writeMainClass() + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + archiveFileName = 'my-shadow.tar' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(jarPath("build/libs/my-shadow.tar")).useAll { + containsEntries( + "shadow/Main.class", + "junit/framework/Test.class", + ) + } + } + private fun writeShadowedClientAndServerModules() { writeClientAndServerModules() path("client/build.gradle").appendText( From 96fe2fec2cae1920c6ef9eeae6c605a75e556b1d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 12 Jan 2025 03:28:43 -0500 Subject: [PATCH 141/941] Check Class-Path in shadowed Gradle plugin jar (#1149) --- .../gradle/plugins/shadow/ShadowPluginTest.kt | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt index 91f9d778d..d73eee5b4 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -3,10 +3,13 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.all import assertk.assertThat import assertk.assertions.contains +import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo +import assertk.assertions.isNotEmpty import assertk.assertions.isNotNull import assertk.assertions.isNull import assertk.assertions.isTrue +import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar @@ -738,6 +741,7 @@ class ShadowPluginTest : BasePluginTest() { @Issue( "https://github.com/GradleUp/shadow/issues/459", + "https://github.com/GradleUp/shadow/issues/852", ) @Test fun excludeGradleApiByDefault() { @@ -765,8 +769,15 @@ class ShadowPluginTest : BasePluginTest() { run(shadowJarTask) - val entries = outputShadowJar.use { it.entries().toList() } - assertThat(entries.count { it.name.endsWith(".class") }).isEqualTo(1) + assertThat(outputShadowJar).useAll { + transform { actual -> actual.entries().toList().map { it.name }.filter { it.endsWith(".class") } } + .single().isEqualTo("my/plugin/MyPlugin.class") + transform { it.manifest.mainAttributes.keys }.all { + isNotEmpty() + // Doesn't contain Gradle classes. + doesNotContain("Class-Path") + } + } } @Issue( From a2f9fb9cfdf01d498253577ad13c47b76d4b25b5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 12 Jan 2025 08:14:23 -0500 Subject: [PATCH 142/941] Standalone XmlAppendingTransformerTest (#1151) * Add XmlAppendingTransformerTest * Add a test for checking META-INF/plugin.xml --- .../shadow/transformers/TransformersTest.kt | 42 ----------- .../XmlAppendingTransformerTest.kt | 75 +++++++++++++++++++ 2 files changed, 75 insertions(+), 42 deletions(-) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 551578cce..f16e4265d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -48,48 +48,6 @@ class TransformersTest : BaseTransformerTest() { commonAssertions() } - @Test - fun appendXmlFiles() { - val propertiesXml = "properties.xml" - val xmlContent = """ - - - - %s - - """.trimIndent() - - val xml1 = buildJar("xml1.jar") { - insert(propertiesXml, xmlContent.format("key1", "val1")) - } - val xml2 = buildJar("xml2.jar") { - insert(propertiesXml, xmlContent.format("key2", "val2")) - } - - projectScriptPath.appendText( - transform( - shadowJarBlock = fromJar(xml1, xml2), - transformerBlock = """ - resource = 'properties.xml' - """.trimIndent(), - ), - ) - - run(shadowJarTask) - - val content = outputShadowJar.use { it.getContent(propertiesXml) }.trimIndent() - assertThat(content).isEqualTo( - """ - - - - val1 - val2 - - """.trimIndent(), - ) - } - @Issue("https://github.com/GradleUp/shadow/issues/82") @Test fun shadowManifestLeaksToJarManifest() { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt new file mode 100644 index 000000000..dded30087 --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -0,0 +1,75 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.isEqualTo +import kotlin.io.path.appendText +import org.junit.jupiter.api.Test + +class XmlAppendingTransformerTest : BaseTransformerTest() { + @Test + fun appendXmlFiles() { + val propertiesXml = "properties.xml" + val xmlContent = """ + + + + %s + + """.trimIndent() + + val xml1 = buildJar("xml1.jar") { + insert(propertiesXml, xmlContent.format("key1", "val1")) + } + val xml2 = buildJar("xml2.jar") { + insert(propertiesXml, xmlContent.format("key2", "val2")) + } + + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar(xml1, xml2), + transformerBlock = """ + resource = 'properties.xml' + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + val content = outputShadowJar.use { it.getContent(propertiesXml) }.trimIndent() + assertThat(content).isEqualTo( + """ + + + + val1 + val2 + + """.trimIndent(), + ) + } + + @Test + fun canBundleMetaInfoPluginXml() { + val xmlEntry = "META-INF/plugin.xml" + val xmlContent = """ + + + my.plugin.id + + """.trimIndent() + val pluginJar = buildJar("plugin.jar") { + insert(xmlEntry, xmlContent) + } + + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar(pluginJar), + ), + ) + + run(shadowJarTask) + + val content = outputShadowJar.use { it.getContent(xmlEntry) }.trimIndent() + assertThat(content).isEqualTo(xmlContent) + } +} From ecab3c46d24e9915728f22269cd287cae20fb309 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 12 Jan 2025 21:36:28 -0500 Subject: [PATCH 143/941] Tweak disabled functional tests (#1153) * Remove disabled checkLargeZipFilesWithZip64Enabled * Replace EnabledForJreRange with DisabledForJreRange --- .../gradle/plugins/shadow/ShadowPluginTest.kt | 73 +------------------ 1 file changed, 3 insertions(+), 70 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt index d73eee5b4..94361f328 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt @@ -25,9 +25,8 @@ import org.gradle.testfixtures.ProjectBuilder import org.gradle.testkit.runner.TaskOutcome.FAILED import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test -import org.junit.jupiter.api.condition.EnabledForJreRange +import org.junit.jupiter.api.condition.DisabledForJreRange import org.junit.jupiter.api.condition.JRE class ShadowPluginTest : BasePluginTest() { @@ -59,8 +58,8 @@ class ShadowPluginTest : BasePluginTest() { } @Test - @EnabledForJreRange( - max = JRE.JAVA_21, + @DisabledForJreRange( + min = JRE.JAVA_21, disabledReason = "Gradle 8.3 doesn't support Java 21.", ) fun compatibleWithMinGradleVersion() { @@ -639,72 +638,6 @@ class ShadowPluginTest : BasePluginTest() { assertThat(outputShadowJar).isRegular() } - /** - * This spec requires > 15 minutes and > 8GB of disk space to run - */ - @Issue( - "https://github.com/GradleUp/shadow/issues/143", - ) - @Disabled - @Test - fun checkLargeZipFilesWithZip64Enabled() { - path("src/main/java/myapp/Main.java").writeText( - """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.trimIndent(), - ) - - settingsScriptPath.appendText("rootProject.name = 'myapp'") - projectScriptPath.appendText( - """ - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - dependencies { - implementation 'shadow:a:1.0' - } - def generatedResourcesDir = new File(project.layout.buildDirectory.asFile.get(), "generated-resources") - def generateResources = tasks.register('generateResources') { - doLast { - def rnd = new Random() - def buf = new byte[128 * 1024] - for (x in 0..255) { - def dir = new File(generatedResourcesDir, x.toString()) - dir.mkdirs() - for (y in 0..255) { - def file = new File(dir, y.toString()) - rnd.nextBytes(buf) - file.bytes = buf - } - } - } - } - sourceSets { - main { - output.dir(generatedResourcesDir, builtBy: generateResources) - } - } - $shadowJar { - zip64 = true - } - $runShadow { - args 'foo' - } - """.trimIndent(), - ) - - val result = run(runShadowTask) - - assertThat(result.output).contains("TestApp: Hello World! (foo)") - } - @Issue( "https://github.com/GradleUp/shadow/issues/609", ) From e8027bcf19575a276acede984a8c20ad4c3dd148 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 12 Jan 2025 22:48:36 -0500 Subject: [PATCH 144/941] Polish Shadow plugin tests (#1152) * Move doesNotErrorWhenUsingApplicationMainClassProperty and checkLargeZipFilesWithZip64Enabled * Simplify doesNotErrorWhenUsingApplicationMainClassProperty * Simplify checkLargeZipFilesWithZip64Enabled * Remove checkLargeZipFilesWithZip64Enabled as it's disabled * Add dependenciesBlock slot in prepare * Use containsExactly for shadowApplicationDistributionsShouldUseShadowJar * Remove doesNotErrorWhenUsingApplicationMainClassProperty as it's redundant * Inline runShadow values * Rename ShadowPluginTest to JavaPluginTest * Rename ApplicationTest to ApplicationPluginTest * Bump org.gradle.toolchains.foojay-resolver-convention to 0.9.0 * Assert more entries in shadowApplicationDistributionsShouldUseShadowJar * Reuse ZipFile.getContent and remove libs.apache.ant * Add commonAssertions --- build.gradle.kts | 1 - ...cationTest.kt => ApplicationPluginTest.kt} | 82 ++++++++++++------- .../gradle/plugins/shadow/BasePluginTest.kt | 7 -- ...{ShadowPluginTest.kt => JavaPluginTest.kt} | 36 +------- .../transformers/AppendingTransformerTest.kt | 1 + .../GroovyExtensionModuleTransformerTest.kt | 1 + .../XmlAppendingTransformerTest.kt | 1 + .../gradle/plugins/shadow/util/JarPath.kt | 14 +++- 8 files changed, 67 insertions(+), 76 deletions(-) rename src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{ApplicationTest.kt => ApplicationPluginTest.kt} (53%) rename src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{ShadowPluginTest.kt => JavaPluginTest.kt} (96%) diff --git a/build.gradle.kts b/build.gradle.kts index 9fa451fde..ff1cb6d97 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -100,7 +100,6 @@ testing.suites { dependencies { // Seems we can't ref project() here due to some limitations of rootProject. implementation(sourceSets.main.get().output) - implementation(libs.apache.ant) implementation(libs.apache.maven.modelBuilder) implementation(libs.moshi) implementation(libs.moshi.kotlin) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt similarity index 53% rename from src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 8f3ea3e97..720df401b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -3,19 +3,24 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.all import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.containsAtLeast +import assertk.assertions.containsOnly import assertk.assertions.exists import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.getContent import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr -import com.github.jengelman.gradle.plugins.shadow.util.isRegular +import com.github.jengelman.gradle.plugins.shadow.util.getStream +import java.util.zip.ZipFile import kotlin.io.path.appendText +import kotlin.io.path.outputStream import kotlin.io.path.readText import kotlin.io.path.writeText -import org.apache.tools.zip.ZipFile import org.junit.jupiter.api.Test -class ApplicationTest : BasePluginTest() { +class ApplicationPluginTest : BasePluginTest() { @Test fun integrationWithApplicationPluginAndJavaToolchains() { prepare( @@ -26,7 +31,7 @@ class ApplicationTest : BasePluginTest() { """.trimIndent(), settingsBlock = """ plugins { - id('org.gradle.toolchains.foojay-resolver-convention') version '0.7.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0' } """.trimIndent(), runShadowBlock = """ @@ -36,21 +41,14 @@ class ApplicationTest : BasePluginTest() { """.trimIndent(), ) - val result = run(runShadowTask) + val result = run(SHADOW_RUN_TASK_NAME) assertThat(result.output).contains( "Running application with JDK 17", "TestApp: Hello World! (foo)", ) - assertThat(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")).useAll { - containsEntries( - "a.properties", - "a2.properties", - "myapp/Main.class", - ) - getMainAttr("Main-Class").isEqualTo("myapp.Main") - } + commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) assertThat(path("build/install/myapp-shadow/bin/myapp")).all { exists() @@ -71,37 +69,49 @@ class ApplicationTest : BasePluginTest() { @Test fun shadowApplicationDistributionsShouldUseShadowJar() { prepare( - projectBlock = """ - dependencies { - shadow 'shadow:a:1.0' - } - """.trimIndent(), + dependenciesBlock = "shadow 'shadow:a:1.0'", ) run("shadowDistZip") - val zip = path("build/distributions/myapp-shadow-1.0.zip") - assertThat(zip).exists() + ZipFile(path("build/distributions/myapp-shadow-1.0.zip").toFile()).use { zip -> + val fileEntries = zip.entries().toList().map { it.name }.filter { !it.endsWith("/") } + assertThat(fileEntries).containsOnly( + "myapp-shadow-1.0/bin/myapp", + "myapp-shadow-1.0/bin/myapp.bat", + "myapp-shadow-1.0/lib/myapp-1.0-all.jar", + "myapp-shadow-1.0/lib/a-1.0.jar", + ) - val entries = ZipFile(zip.toFile()).use { it.entries }.toList().map { it.name } - assertThat(entries).containsAtLeast( - "myapp-shadow-1.0/lib/myapp-1.0-all.jar", - "myapp-shadow-1.0/lib/a-1.0.jar", - ) + val extractedJar = path("extracted/myapp-1.0-all.jar") + zip.getStream("myapp-shadow-1.0/lib/myapp-1.0-all.jar") + .use { it.copyTo(extractedJar.outputStream()) } + commonAssertions(JarPath(extractedJar), entriesContained = arrayOf("myapp/Main.class")) + + assertThat(zip.getContent("myapp-shadow-1.0/bin/myapp")).contains( + "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", + "-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"", + "exec \"\$JAVACMD\" \"\$@\"", + ) + assertThat(zip.getContent("myapp-shadow-1.0/bin/myapp.bat")).contains( + "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", + ) + } } @Test fun installShadowDoesNotExecuteDependentShadowTask() { prepare() - run(ShadowApplicationPlugin.SHADOW_INSTALL_TASK_NAME) + run(SHADOW_INSTALL_TASK_NAME) - assertThat(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")).isRegular() + commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) } private fun prepare( projectBlock: String = "", settingsBlock: String = "", + dependenciesBlock: String = "implementation 'shadow:a:1.0'", runShadowBlock: String = "", ) { path("src/main/java/myapp/Main.java").appendText( @@ -122,7 +132,7 @@ class ApplicationTest : BasePluginTest() { mainClass = 'myapp.Main' } dependencies { - implementation 'shadow:a:1.0' + $dependenciesBlock } $runShadow { args 'foo' @@ -137,4 +147,18 @@ class ApplicationTest : BasePluginTest() { ), ) } + + private fun commonAssertions( + jarPath: JarPath, + entriesContained: Array = arrayOf("a.properties", "a2.properties", "myapp/Main.class"), + ) { + assertThat(jarPath).useAll { + containsEntries(*entriesContained) + getMainAttr("Main-Class").isEqualTo("myapp.Main") + } + } + + private companion object { + val runShadow = "tasks.named('$SHADOW_RUN_TASK_NAME')".trim() + } } 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 2564ddfa3..1eabb1171 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 @@ -6,9 +6,7 @@ import assertk.assertThat import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import assertk.assertions.isNotNull -import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME -import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository @@ -81,7 +79,6 @@ abstract class BasePluginTest { } open val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" - open val runShadowTask = ":$SHADOW_RUN_TASK_NAME" val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" val projectScriptPath: Path get() = path("build.gradle") @@ -323,10 +320,6 @@ abstract class BasePluginTest { tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name}) """.trimIndent() - val runShadow = """ - tasks.named('$SHADOW_RUN_TASK_NAME', ${JavaJarExec::class.java.name}) - """.trimIndent() - val commonArguments = listOf( "--warning-mode=fail", "--configuration-cache", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt similarity index 96% rename from src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 94361f328..2171debe3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -29,7 +29,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledForJreRange import org.junit.jupiter.api.condition.JRE -class ShadowPluginTest : BasePluginTest() { +class JavaPluginTest : BasePluginTest() { @Test fun applyPlugin() { val projectName = "my-shadow" @@ -638,40 +638,6 @@ class ShadowPluginTest : BasePluginTest() { assertThat(outputShadowJar).isRegular() } - @Issue( - "https://github.com/GradleUp/shadow/issues/609", - ) - @Test - fun doesNotErrorWhenUsingApplicationMainClassProperty() { - projectScriptPath.appendText( - """ - apply plugin: 'application' - - application { - mainClass = 'myapp.Main' - } - $runShadow { - args 'foo' - } - """.trimIndent(), - ) - - path("src/main/java/myapp/Main.java").writeText( - """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.trimIndent(), - ) - - val result = run(runShadowTask) - - assertThat(result.output).contains("TestApp: Hello World! (foo)") - } - @Issue( "https://github.com/GradleUp/shadow/issues/459", "https://github.com/GradleUp/shadow/issues/852", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 04807f7f4..a32a8aa9b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import org.junit.jupiter.api.Test diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index c373895f5..a13a43dbb 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -10,6 +10,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_VERSION import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR +import com.github.jengelman.gradle.plugins.shadow.util.getContent import java.nio.file.Path import kotlin.io.path.appendText import org.junit.jupiter.api.Test diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index dded30087..41f215257 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import org.junit.jupiter.api.Test diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index 9e10f3395..ec66a9136 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -4,8 +4,10 @@ import assertk.Assert import assertk.all import assertk.assertions.isNotEmpty import assertk.fail +import java.io.InputStream import java.nio.file.Path import java.util.jar.JarFile +import java.util.zip.ZipFile /** * A wrapper for [JarFile] that also implements [Path]. @@ -20,11 +22,15 @@ class JarPath(val path: Path) : fun getMainAttr(name: String): String? { return manifest.mainAttributes.getValue(name) } +} - fun getContent(entryName: String): String { - val entry = getEntry(entryName) ?: error("Entry not found: $entryName") - return getInputStream(entry).bufferedReader().use { it.readText() } - } +fun ZipFile.getContent(entryName: String): String { + return getStream(entryName).bufferedReader().use { it.readText() } +} + +fun ZipFile.getStream(entryName: String): InputStream { + val entry = getEntry(entryName) ?: error("Entry not found: $entryName") + return getInputStream(entry) } /** From 73e5b484b7943dceeafc5767bf57f2fdc7b9c238 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 13 Jan 2025 21:53:27 -0500 Subject: [PATCH 145/941] Clean up PublishingTest (#1154) * Optimize assertShadowVariantCommon * Replace const values * Check variant name only * Remove unnecessary size checks * Renames --- .../gradle/plugins/shadow/PublishingTest.kt | 49 ++++++++++--------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 167ba3626..4d42a48de 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -4,6 +4,8 @@ import assertk.assertThat import assertk.assertions.containsOnly import assertk.assertions.isEmpty import assertk.assertions.isEqualTo +import assertk.assertions.single +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath @@ -23,6 +25,8 @@ import org.apache.maven.model.Model import org.apache.maven.model.io.xpp3.MavenXpp3Reader import org.gradle.api.attributes.Bundling import org.gradle.api.attributes.Usage +import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME import org.gradle.testkit.runner.BuildResult import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -171,46 +175,45 @@ class PublishingTest : BasePluginTest() { containsEntries(*entries) } - pomReader.read(repoPath("com/acme/maven/1.0/maven-1.0.pom")).let { pomContents -> - assertThat(pomContents.dependencies.size).isEqualTo(2) - pomContents.dependencies[0].let { dependency -> + pomReader.read(repoPath("com/acme/maven/1.0/maven-1.0.pom")).let { pom -> + assertThat(pom.dependencies.size).isEqualTo(2) + pom.dependencies[0].let { dependency -> assertThat(dependency.groupId).isEqualTo("shadow") assertThat(dependency.artifactId).isEqualTo("a") assertThat(dependency.version).isEqualTo("1.0") } - pomContents.dependencies[1].let { dependency -> + pom.dependencies[1].let { dependency -> assertThat(dependency.groupId).isEqualTo("shadow") assertThat(dependency.artifactId).isEqualTo("b") assertThat(dependency.version).isEqualTo("1.0") } } - gmmAdapter.fromJson(repoPath("com/acme/maven/1.0/maven-1.0.module")).let { gmmContents -> - assertThat(gmmContents.variants.size).isEqualTo(3) - assertThat(gmmContents.variants.map { it.name }).containsOnly( - "apiElements", - "runtimeElements", - "shadowRuntimeElements", + gmmAdapter.fromJson(repoPath("com/acme/maven/1.0/maven-1.0.module")).let { gmm -> + // apiElements, runtimeElements, shadowRuntimeElements + assertThat(gmm.variants.map { it.name }).containsOnly( + API_ELEMENTS_CONFIGURATION_NAME, + RUNTIME_ELEMENTS_CONFIGURATION_NAME, + SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) - val apiVariant = gmmContents.variants.single { it.name == "apiElements" } + val apiVariant = gmm.variants.single { it.name == API_ELEMENTS_CONFIGURATION_NAME } assertThat(apiVariant.attributes[Usage.USAGE_ATTRIBUTE.name]).isEqualTo(Usage.JAVA_API) assertThat(apiVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name]).isEqualTo(Bundling.EXTERNAL) assertThat(apiVariant.dependencies).isEmpty() - val runtimeVariant = gmmContents.variants.single { it.name == "runtimeElements" } + val runtimeVariant = gmm.variants.single { it.name == RUNTIME_ELEMENTS_CONFIGURATION_NAME } assertThat(runtimeVariant.attributes[Usage.USAGE_ATTRIBUTE.name]).isEqualTo(Usage.JAVA_RUNTIME) assertThat(runtimeVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name]).isEqualTo(Bundling.EXTERNAL) - assertThat(runtimeVariant.dependencies.size).isEqualTo(2) assertThat(runtimeVariant.dependencies.map { it.module }).containsOnly("a", "b") - assertShadowVariantCommon(gmmContents.variants.single { it.name == "shadowRuntimeElements" }) + assertShadowVariantCommon(gmm) } assertPomCommon(repoPath("com/acme/maven-all/1.0/maven-all-1.0.pom")) - gmmAdapter.fromJson(repoPath("com/acme/maven-all/1.0/maven-all-1.0.module")).let { gmmContents -> - assertThat(gmmContents.variants.size).isEqualTo(1) - assertShadowVariantCommon(gmmContents.variants.single { it.name == "shadowRuntimeElements" }) + gmmAdapter.fromJson(repoPath("com/acme/maven-all/1.0/maven-all-1.0.module")).let { gmm -> + assertThat(gmm.variants).single().transform { it.name }.isEqualTo(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) + assertShadowVariantCommon(gmm) } } @@ -264,20 +267,18 @@ class PublishingTest : BasePluginTest() { } private fun assertPomCommon(pomPath: Path) { - val contents = pomReader.read(pomPath) - assertThat(contents.dependencies.size).isEqualTo(1) - - val dependency = contents.dependencies[0] + val pom = pomReader.read(pomPath) + val dependency = pom.dependencies.single() assertThat(dependency.groupId).isEqualTo("shadow") assertThat(dependency.artifactId).isEqualTo("b") assertThat(dependency.version).isEqualTo("1.0") } - private fun assertShadowVariantCommon(variant: GradleModuleMetadata.Variant) { + private fun assertShadowVariantCommon(gmm: GradleModuleMetadata) { + val variant = gmm.variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME } assertThat(variant.attributes[Usage.USAGE_ATTRIBUTE.name]).isEqualTo(Usage.JAVA_RUNTIME) assertThat(variant.attributes[Bundling.BUNDLING_ATTRIBUTE.name]).isEqualTo(Bundling.SHADOWED) - assertThat(variant.dependencies.size).isEqualTo(1) - assertThat(variant.dependencies.map { it.module }).containsOnly("b") + assertThat(variant.dependencies).single().transform { it.module }.isEqualTo("b") } private fun assertShadowJarCommon(jarPath: JarPath) { From 82c4557ddcc841bf2a53e7591c93f5cd125cf860 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 13 Jan 2025 22:40:13 -0500 Subject: [PATCH 146/941] Show how to publish the shadowed jar only (#1150) * Add publishShadowJarInsteadOfJarWithMavenPublishPlugin test * Add an example in src/docs/publishing/README.md --- src/docs/publishing/README.md | 42 +++++++++++++++++++ .../gradle/plugins/shadow/PublishingTest.kt | 32 ++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/docs/publishing/README.md b/src/docs/publishing/README.md index d87ccf8fc..e912b5137 100644 --- a/src/docs/publishing/README.md +++ b/src/docs/publishing/README.md @@ -44,3 +44,45 @@ configuration of the POM file. This automatic configuration occurs _only_ when using the above methods for configuring publishing. If this behavior is not desirable, then publishing **must** be manually configured. + + +## Publish the Shadowed JAR instead of the Original JAR + +You may want to publish the shadowed JAR instead of the original JAR. This can be done by trimming +the `archiveClassifier` of the shadowed JAR like the following: + +```groovy +apply plugin: 'java' +apply plugin: 'maven-publish' +apply plugin: 'com.gradleup.shadow' + +group = 'shadow' +version = '1.0' + +dependencies { + // This will be bundled in the shadowed JAR and not declared in the POM. + implementation 'some:a:1.0' + // This will be excluded from the shadowed JAR but declared as a runtime dependency in `META-INF/MANIFEST.MF` + // file's `Class-Path` entry, and also in the POM file. + shadow 'some:b:1.0' + // This will be excluded from the shadowed JAR and not declared in the POM or `META-INF/MANIFEST.MF`. + compileOnly 'some:c:1.0' +} + +tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + archiveClassifier = '' +} + +publishing { + publications { + shadow(MavenPublication) { + from components.shadow + } + } + repositories { + maven { + url = "https://repo.myorg.com" + } + } +} +``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 4d42a48de..18cd42fe6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -11,6 +11,7 @@ import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory @@ -62,6 +63,37 @@ class PublishingTest : BasePluginTest() { assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) } + @Test + fun publishShadowJarInsteadOfJarWithMavenPublishPlugin() { + projectScriptPath.appendText( + publishConfiguration( + shadowBlock = """ + archiveClassifier = '' + """.trimIndent(), + publicationsBlock = """ + shadow(MavenPublication) { + from components.shadow + } + """.trimIndent(), + ), + ) + + publish() + + assertThat(repoJarPath("shadow/maven/1.0/maven-1.0.jar")).useAll { + containsEntries( + "a.properties", + "a2.properties", + ) + doesNotContainEntries( + "b.properties", + ) + getMainAttr("Class-Path").isEqualTo("b-1.0.jar") + } + assertPomCommon(repoPath("shadow/maven/1.0/maven-1.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven/1.0/maven-1.0.module"))) + } + @Issue( "https://github.com/GradleUp/shadow/issues/860", "https://github.com/GradleUp/shadow/issues/945", From 5e6ab6e2e89285a535f54040ba5288891622d217 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 13 Jan 2025 23:26:47 -0500 Subject: [PATCH 147/941] Constraint foojay-resolver version (#946) --- build.gradle.kts | 12 ++++++++++++ gradle/libs.versions.toml | 1 + settings.gradle.kts | 1 + .../gradle/plugins/shadow/ApplicationPluginTest.kt | 2 +- 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index ff1cb6d97..d2f458f92 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -103,6 +103,8 @@ testing.suites { implementation(libs.apache.maven.modelBuilder) implementation(libs.moshi) implementation(libs.moshi.kotlin) + // Used in test `plugins` blocks. + implementation(libs.foojayResolver) } } @@ -126,6 +128,16 @@ gradlePlugin { ) } +tasks.pluginUnderTestMetadata { + val functionalTestImplementation = configurations.named("functionalTestImplementation") { + isCanBeResolved = true + } + // Plugins used in tests could be resolved in classpath. + pluginClasspath.from( + functionalTestImplementation, + ) +} + tasks.check { dependsOn( testing.suites.named("integrationTest"), diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c4b8bdbfe..c30811fc3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,6 +14,7 @@ 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.0" mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:0.30.0" diff --git a/settings.gradle.kts b/settings.gradle.kts index 774c947d5..4bb3ca1a1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,6 +38,7 @@ dependencyResolutionManagement { includeGroupAndSubgroups("com.google") } } + gradlePluginPortal() } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 720df401b..d5f7fdba7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -31,7 +31,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), settingsBlock = """ plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.9.0' + id 'org.gradle.toolchains.foojay-resolver-convention' } """.trimIndent(), runShadowBlock = """ From 3e6f192fbc324fa0ff78477ab1211f67ca705b8f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 14 Jan 2025 00:11:40 -0500 Subject: [PATCH 148/941] Prefer plugins blocks in docs (#1155) --- src/docs/application-plugin/README.md | 16 ++++++++++------ src/docs/getting-started/README.md | 4 +++- src/docs/plugins/README.md | 6 ++++-- src/docs/publishing/README.md | 16 ++++++++++------ .../shadow/executor/GroovyBuildExecutor.kt | 6 ++++-- .../plugins/shadow/fixture/GroovyDslFixture.kt | 2 +- .../plugins/shadow/fixture/SnippetFixture.kt | 2 +- 7 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/docs/application-plugin/README.md b/src/docs/application-plugin/README.md index c1e8110ae..2ce520a96 100644 --- a/src/docs/application-plugin/README.md +++ b/src/docs/application-plugin/README.md @@ -9,9 +9,11 @@ configured to contain the `Main-Class` attribute with the value specified in the ```groovy // Using Shadow with Application Plugin -apply plugin: 'java' -apply plugin: 'application' -apply plugin: 'com.gradleup.shadow' +plugins { + id 'java' + id 'application' + id 'com.gradleup.shadow' +} application { mainClass = 'myapp.Main' @@ -28,9 +30,11 @@ It can be configured the same as any other `JavaExec` task. ```groovy // Configuring the runShadow Task -apply plugin: 'java' -apply plugin: 'application' -apply plugin: 'com.gradleup.shadow' +plugins { + id 'java' + id 'application' + id 'com.gradleup.shadow' +} application { mainClass = 'myapp.Main' diff --git a/src/docs/getting-started/README.md b/src/docs/getting-started/README.md index 6df8741a8..ae53b93dc 100644 --- a/src/docs/getting-started/README.md +++ b/src/docs/getting-started/README.md @@ -19,8 +19,9 @@ buildscript { } } -apply plugin: 'com.gradleup.shadow' +// `apply plugin` stuffs are used with `buildscript`. apply plugin: 'java' +apply plugin: 'com.gradleup.shadow' ```
@@ -38,6 +39,7 @@ buildscript { } } +// `apply plugin` stuffs are used with `buildscript`. apply plugin: 'java' apply plugin: 'com.gradleup.shadow' ``` diff --git a/src/docs/plugins/README.md b/src/docs/plugins/README.md index e65c9534e..d0e107bf4 100644 --- a/src/docs/plugins/README.md +++ b/src/docs/plugins/README.md @@ -11,8 +11,10 @@ and `relocationPrefix` settings on any `ShadowJar` task. A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the `shadowJar` task for relocation. ```groovy -apply plugin: 'java-gradle-plugin' -apply plugin: 'com.gradleup.shadow' +plugins { + id 'java-gradle-plugin' + id 'com.gradleup.shadow' +} dependencies { implementation 'org.jdom:jdom2:2.0.6' diff --git a/src/docs/publishing/README.md b/src/docs/publishing/README.md index e912b5137..15172841d 100644 --- a/src/docs/publishing/README.md +++ b/src/docs/publishing/README.md @@ -9,9 +9,11 @@ artifact and dependencies in the POM file. ```groovy // Publishing a Shadow JAR with the Maven-Publish Plugin -apply plugin: 'java' -apply plugin: 'maven-publish' -apply plugin: 'com.gradleup.shadow' +plugins { + id 'java' + id 'maven-publish' + id 'com.gradleup.shadow' +} publishing { publications { @@ -52,9 +54,11 @@ You may want to publish the shadowed JAR instead of the original JAR. This can b the `archiveClassifier` of the shadowed JAR like the following: ```groovy -apply plugin: 'java' -apply plugin: 'maven-publish' -apply plugin: 'com.gradleup.shadow' +plugins { + id 'java' + id 'maven-publish' + id 'com.gradleup.shadow' +} group = 'shadow' version = '1.0' diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt index fbe45440d..aac341f25 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt @@ -36,8 +36,10 @@ class GroovyBuildExecutor( val mainScript = buildString { append(imports) append(System.lineSeparator()) - append(fixture.pre) - append(System.lineSeparator()) + if (!snippetWithoutImports.contains("plugins {")) { + append(fixture.pluginsBlock) + append(System.lineSeparator()) + } append(snippetWithoutImports) append(System.lineSeparator()) }.trimIndent() diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt index 525dbce85..aef8bc174 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt @@ -2,7 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.fixture object GroovyDslFixture : SnippetFixture { - override val pre: String = """ + override val pluginsBlock: String = """ plugins { id 'java' id 'com.gradleup.shadow' diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt index 95a2ebf5d..d39fc2386 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt @@ -1,5 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.fixture interface SnippetFixture { - val pre: String + val pluginsBlock: String } From 19b7439a14a03a07dbd18e8f2587a2849e345a24 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 14 Jan 2025 10:11:03 -0500 Subject: [PATCH 149/941] Check more attributes in GMM (#1158) --- .../gradle/plugins/shadow/PublishingTest.kt | 53 ++++++++++++------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 18cd42fe6..f37756ed1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -1,6 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow +import assertk.all import assertk.assertThat +import assertk.assertions.contains import assertk.assertions.containsOnly import assertk.assertions.isEmpty import assertk.assertions.isEqualTo @@ -25,6 +27,8 @@ import kotlin.io.path.writeText import org.apache.maven.model.Model import org.apache.maven.model.io.xpp3.MavenXpp3Reader import org.gradle.api.attributes.Bundling +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.LibraryElements import org.gradle.api.attributes.Usage import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME @@ -220,7 +224,6 @@ class PublishingTest : BasePluginTest() { assertThat(dependency.version).isEqualTo("1.0") } } - gmmAdapter.fromJson(repoPath("com/acme/maven/1.0/maven-1.0.module")).let { gmm -> // apiElements, runtimeElements, shadowRuntimeElements assertThat(gmm.variants.map { it.name }).containsOnly( @@ -228,23 +231,32 @@ class PublishingTest : BasePluginTest() { RUNTIME_ELEMENTS_CONFIGURATION_NAME, SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) - - val apiVariant = gmm.variants.single { it.name == API_ELEMENTS_CONFIGURATION_NAME } - assertThat(apiVariant.attributes[Usage.USAGE_ATTRIBUTE.name]).isEqualTo(Usage.JAVA_API) - assertThat(apiVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name]).isEqualTo(Bundling.EXTERNAL) - assertThat(apiVariant.dependencies).isEmpty() - - val runtimeVariant = gmm.variants.single { it.name == RUNTIME_ELEMENTS_CONFIGURATION_NAME } - assertThat(runtimeVariant.attributes[Usage.USAGE_ATTRIBUTE.name]).isEqualTo(Usage.JAVA_RUNTIME) - assertThat(runtimeVariant.attributes[Bundling.BUNDLING_ATTRIBUTE.name]).isEqualTo(Bundling.EXTERNAL) - assertThat(runtimeVariant.dependencies.map { it.module }).containsOnly("a", "b") - + assertThat(gmm.variants.single { it.name == API_ELEMENTS_CONFIGURATION_NAME }).all { + transform { it.attributes }.all { + contains(Category.CATEGORY_ATTRIBUTE.name, Category.LIBRARY) + contains(Bundling.BUNDLING_ATTRIBUTE.name, Bundling.EXTERNAL) + contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name, LibraryElements.JAR) + contains(Usage.USAGE_ATTRIBUTE.name, Usage.JAVA_API) + } + transform { it.dependencies }.isEmpty() + } + assertThat(gmm.variants.single { it.name == RUNTIME_ELEMENTS_CONFIGURATION_NAME }).all { + transform { it.attributes }.all { + contains(Category.CATEGORY_ATTRIBUTE.name, Category.LIBRARY) + contains(Bundling.BUNDLING_ATTRIBUTE.name, Bundling.EXTERNAL) + contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name, LibraryElements.JAR) + contains(Usage.USAGE_ATTRIBUTE.name, Usage.JAVA_RUNTIME) + } + transform { it.dependencies.map { dep -> dep.module } }.containsOnly("a", "b") + } assertShadowVariantCommon(gmm) } - assertPomCommon(repoPath("com/acme/maven-all/1.0/maven-all-1.0.pom")) + assertPomCommon(repoPath("com/acme/maven-all/1.0/maven-all-1.0.pom")) gmmAdapter.fromJson(repoPath("com/acme/maven-all/1.0/maven-all-1.0.module")).let { gmm -> - assertThat(gmm.variants).single().transform { it.name }.isEqualTo(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) + assertThat(gmm.variants.map { it.name }).containsOnly( + SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, + ) assertShadowVariantCommon(gmm) } } @@ -307,10 +319,15 @@ class PublishingTest : BasePluginTest() { } private fun assertShadowVariantCommon(gmm: GradleModuleMetadata) { - val variant = gmm.variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME } - assertThat(variant.attributes[Usage.USAGE_ATTRIBUTE.name]).isEqualTo(Usage.JAVA_RUNTIME) - assertThat(variant.attributes[Bundling.BUNDLING_ATTRIBUTE.name]).isEqualTo(Bundling.SHADOWED) - assertThat(variant.dependencies).single().transform { it.module }.isEqualTo("b") + assertThat(gmm.variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME }).all { + transform { it.attributes }.all { + contains(Category.CATEGORY_ATTRIBUTE.name, Category.LIBRARY) + contains(Bundling.BUNDLING_ATTRIBUTE.name, Bundling.SHADOWED) + contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name, LibraryElements.JAR) + contains(Usage.USAGE_ATTRIBUTE.name, Usage.JAVA_RUNTIME) + } + transform { it.dependencies.map { dep -> dep.module } }.containsOnly("b") + } } private fun assertShadowJarCommon(jarPath: JarPath) { From 1956c31952ececef8ca44c0240719056ca6764cf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Jan 2025 07:23:35 +0800 Subject: [PATCH 150/941] Update plugin spotless to v7.0.2 (#1159) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c30811fc3..fd4963ba6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,4 +33,4 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" kotlin-jvm = "org.jetbrains.kotlin.jvm:2.1.0" android-lint = "com.android.lint:8.8.0" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" -spotless = "com.diffplug.spotless:7.0.1" +spotless = "com.diffplug.spotless:7.0.2" From 927759529ea5801d15716f20d4595a41955c14a3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 14 Jan 2025 22:27:27 -0500 Subject: [PATCH 151/941] Refine unit test names (#1160) * Replace backticks * Trim test prefixes --- .../shadow/relocation/SimpleRelocatorTest.kt | 34 +++++++++---------- .../ApacheLicenseResourceTransformerTest.kt | 2 +- .../ApacheNoticeResourceTransformerTest.kt | 16 ++++----- .../transformers/AppendingTransformerTest.kt | 2 +- .../ComponentsXmlResourceTransformerTest.kt | 2 +- .../Log4j2PluginsCacheFileTransformerTest.kt | 6 ++-- .../ManifestAppenderTransformerTest.kt | 10 +++--- .../PropertiesFileTransformerTest.kt | 10 +++--- .../ServiceFileTransformerTest.kt | 6 ++-- .../XmlAppendingTransformerTest.kt | 2 +- 10 files changed, 45 insertions(+), 45 deletions(-) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 37be193dd..367ab4476 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -15,7 +15,7 @@ import org.junit.jupiter.api.Test class SimpleRelocatorTest { @Test - fun testCanRelocatePath() { + fun canRelocatePath() { var relocator = SimpleRelocator("org.foo") assertThat(relocator.canRelocatePath("org/foo/Class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() @@ -69,7 +69,7 @@ class SimpleRelocatorTest { } @Test - fun testCanRelocatePathWithRegex() { + fun canRelocatePathWithRegex() { // Include with Regex var relocator = SimpleRelocator( "org.foo", @@ -108,7 +108,7 @@ class SimpleRelocatorTest { } @Test - fun testCanRelocateClass() { + fun canRelocateClass() { var relocator = SimpleRelocator("org.foo") assertThat(relocator.canRelocateClass("org.foo.Class")).isTrue() assertThat(relocator.canRelocateClass("org.foo.bar.Class")).isTrue() @@ -137,7 +137,7 @@ class SimpleRelocatorTest { } @Test - fun testCanRelocateRawString() { + fun canRelocateRawString() { var relocator = SimpleRelocator("org/foo", rawString = true) assertThat(relocator.canRelocatePath("(I)org/foo/bar/Class;")).isTrue() @@ -146,14 +146,14 @@ class SimpleRelocatorTest { } @Test - fun testCanRelocateAbsClassPath() { + fun canRelocateAbsClassPath() { val relocator = SimpleRelocator("org.apache.velocity", "org.apache.momentum") assertThat(relocator.relocatePath("/org/apache/velocity/mass.properties")) .isEqualTo("/org/apache/momentum/mass.properties") } @Test - fun testCanRelocateAbsClassPathWithExcludes() { + fun canRelocateAbsClassPathWithExcludes() { val relocator = SimpleRelocator( "org/apache/velocity", "org/apache/momentum", @@ -166,7 +166,7 @@ class SimpleRelocatorTest { } @Test - fun testCanRelocateAbsClassPathWithIncludes() { + fun canRelocateAbsClassPathWithIncludes() { val relocator = SimpleRelocator( "org/apache/velocity", "org/apache/momentum", @@ -179,7 +179,7 @@ class SimpleRelocatorTest { } @Test - fun testRelocatePath() { + fun relocatePath() { var relocator = SimpleRelocator("org.foo") assertThat(relocator.relocatePath("org/foo/bar/Class.class")) .isEqualTo("hidden/org/foo/bar/Class.class") @@ -190,7 +190,7 @@ class SimpleRelocatorTest { } @Test - fun testRelocateClass() { + fun relocateClass() { var relocator = SimpleRelocator("org.foo") assertThat(relocator.relocateClass("org.foo.bar.Class")) .isEqualTo("hidden.org.foo.bar.Class") @@ -201,7 +201,7 @@ class SimpleRelocatorTest { } @Test - fun testRelocateRawString() { + fun relocateRawString() { var relocator = SimpleRelocator("Lorg/foo", "Lhidden/org/foo", rawString = true) assertThat(relocator.relocatePath("(I)Lorg/foo/bar/Class;")) .isEqualTo("(I)Lhidden/org/foo/bar/Class;") @@ -212,7 +212,7 @@ class SimpleRelocatorTest { } @Test - fun testRelocateMavenFiles() { + fun relocateMavenFiles() { val relocator = SimpleRelocator( "META-INF/maven", "META-INF/shade/maven", @@ -227,7 +227,7 @@ class SimpleRelocatorTest { } @Test - fun testCanRelocateExcludedSourceFile() { + fun canRelocateExcludedSourceFile() { val relocator = SimpleRelocator( "org.foo", excludes = listOf("org/apache/iceberg/spark/parquet/**", "org/apache/spark/sql/execution/datasources/parquet/**"), @@ -239,7 +239,7 @@ class SimpleRelocatorTest { } @Test - fun testCanRelocateExcludedSourceFileWithRegex() { + fun canRelocateExcludedSourceFileWithRegex() { val relocator = SimpleRelocator( "org.foo", excludes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]"), @@ -251,7 +251,7 @@ class SimpleRelocatorTest { } @Test - fun testCanRelocateIncludedSourceFile() { + fun canRelocateIncludedSourceFile() { val relocator = SimpleRelocator( includes = listOf("org/apache/iceberg/spark/parquet/**", "org/apache/spark/sql/execution/datasources/parquet/**"), ) @@ -262,7 +262,7 @@ class SimpleRelocatorTest { } @Test - fun testCanRelocateIncludedSourceFileWithRegex() { + fun canRelocateIncludedSourceFileWithRegex() { val relocator = SimpleRelocator( includes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]"), ) @@ -273,7 +273,7 @@ class SimpleRelocatorTest { } @Test - fun testRelocateSourceWithExcludesRaw() { + fun relocateSourceWithExcludesRaw() { val relocator = SimpleRelocator( "org.apache.maven", "com.acme.maven", @@ -285,7 +285,7 @@ class SimpleRelocatorTest { } @Test - fun testRelocateSourceWithExcludes() { + fun relocateSourceWithExcludes() { // Main relocator with in-/excludes val relocator = SimpleRelocator( "org.apache.maven", diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt index 3c2782e61..556e9c6bc 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt @@ -15,7 +15,7 @@ class ApacheLicenseResourceTransformerTest : BaseTransformerTest() { } @Test - fun testCanTransformResource() { + fun canTransformResource() { transformer.resource.set("abcdefghijklmnopqrstuvwxyz") assertThat(transformer.canTransformResource("abcdefghijklmnopqrstuvwxyz")).isTrue() diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt index 73b68d961..dac3e6d02 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test */ class ComponentsXmlResourceTransformerTest : BaseTransformerTest() { @Test - fun testConfigurationMerging() { + fun configurationMerging() { XMLUnit.setNormalizeWhitespace(true) transformer.transform( diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index 945b9bc8d..3a24dec71 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -15,19 +15,19 @@ import org.junit.jupiter.api.Test class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest() { @Test - fun `should transform`() { + fun shouldTransform() { transformer.transform(context(SimpleRelocator())) assertThat(transformer.hasTransformedResource()).isTrue() } @Test - fun `should transform for a single file`() { + fun shouldTransformForSingleFile() { transformer.transform(context()) assertThat(transformer.hasTransformedResource()).isTrue() } @Test - fun `relocate classes inside DAT file`() { + fun relocateClassesInsideDatFile() { val relocator = SimpleRelocator("org.apache.logging", "new.location.org.apache.logging") transformer.transform(context(relocator)) assertThat(transformer.hasTransformedResource()).isTrue() diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt index 26d2bc752..d4de4d1d3 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test class ManifestAppenderTransformerTest : BaseTransformerTest() { @Test - fun testCanTransformResource() { + fun canTransformResource() { with(transformer) { append("Name", "org/foo/bar/") append("Sealed", true) @@ -22,19 +22,19 @@ class ManifestAppenderTransformerTest : BaseTransformerTest() { @Test - fun testHasTransformedResource() { + fun hasTransformedResource() { transformer.transform(manifestTransformerContext) assertThat(transformer.hasTransformedResource()).isTrue() } @Test - fun testHasNotTransformedResource() { + fun hasNotTransformedResource() { assertThat(transformer.hasTransformedResource()).isFalse() } @Test - fun testTransformation() { + fun transformation() { transformer.transform(manifestTransformerContext) val testableZipPath = doTransformAndGetTransformedPath(transformer, false) @@ -41,7 +41,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { @ParameterizedTest(name = "{index} => path={0}, exclude={1}, expected={2}") @MethodSource("canTransformResourceData") - fun testCanTransformResource(path: String, exclude: Boolean, expected: Boolean) { + fun canTransformResource(path: String, exclude: Boolean, expected: Boolean) { if (exclude) { transformer.exclude(path) } @@ -21,7 +21,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() @ParameterizedTest(name = "{index} => path={0}") @MethodSource("transformsServiceFileData") - fun `test transforms service file`(path: String, input1: String, input2: String, output: String) { + fun transformServiceFile(path: String, input1: String, input2: String, output: String) { if (transformer.canTransformResource(path)) { transformer.transform(context(path, input1)) transformer.transform(context(path, input2)) @@ -33,7 +33,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() } @Test - fun `test excludes Groovy extension module descriptor files by default`() { + fun excludesGroovyExtensionModuleDescriptorFilesByDefault() { val element = "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" assertThat(transformer.canTransformResource(element)).isFalse() } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index eddd1a2d0..3fc6ddb39 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -12,7 +12,7 @@ class XmlAppendingTransformerTest : BaseTransformerTest } @Test - fun testCanTransformResource() { + fun canTransformResource() { transformer.resource.set("abcdefghijklmnopqrstuvwxyz") assertThat(transformer.canTransformResource("abcdefghijklmnopqrstuvwxyz")).isTrue() From 94fc07edaef6efcd57b2b0ad9cad18cd8a847c74 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 16 Jan 2025 09:29:22 -0500 Subject: [PATCH 152/941] Check more cases for shadowed Gradle plugin (#1161) * Extract writeGradlePluginModule * Overload for useProperties * Tweak checks * Cleanups --- .../gradle/plugins/shadow/BasePluginTest.kt | 43 +++++++++++++++++ .../gradle/plugins/shadow/JavaPluginTest.kt | 47 +++++++++---------- 2 files changed, 65 insertions(+), 25 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 1eabb1171..bf956d417 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 @@ -289,6 +289,49 @@ abstract class BasePluginTest { ) } + fun writeGradlePluginModule(legacy: Boolean) { + val pluginId = "my.plugin" + val pluginClass = "my.plugin.MyPlugin" + val gradlePluginBlock: String + + if (legacy) { + gradlePluginBlock = "" + path("src/main/resources/META-INF/gradle-plugins/$pluginId.properties") + .writeText("implementation-class=$pluginClass") + } else { + gradlePluginBlock = """ + gradlePlugin { + plugins { + create("myPlugin") { + id = '$pluginId' + implementationClass = '$pluginClass' + } + } + } + """.trimIndent() + } + + projectScriptPath.writeText( + """ + ${getDefaultProjectBuildScript("java-gradle-plugin", withGroup = true, withVersion = true)} + $gradlePluginBlock + """.trimIndent() + System.lineSeparator(), + ) + + path("src/main/java/my/plugin/MyPlugin.java").writeText( + """ + package my.plugin; + import org.gradle.api.Plugin; + import org.gradle.api.Project; + public class MyPlugin implements Plugin { + public void apply(Project project) { + System.out.println("MyPlugin: Hello World!"); + } + } + """.trimIndent(), + ) + } + fun runner( arguments: Iterable = emptyList(), projectDir: Path? = projectRoot, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 2171debe3..a870adf68 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.all import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import assertk.assertions.isNotEmpty import assertk.assertions.isNotNull @@ -16,6 +15,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.isRegular import kotlin.io.path.appendText import kotlin.io.path.readText @@ -28,6 +28,8 @@ import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledForJreRange import org.junit.jupiter.api.condition.JRE +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class JavaPluginTest : BasePluginTest() { @Test @@ -642,40 +644,35 @@ class JavaPluginTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/459", "https://github.com/GradleUp/shadow/issues/852", ) - @Test - fun excludeGradleApiByDefault() { - projectScriptPath.writeText( - getDefaultProjectBuildScript("java-gradle-plugin", withGroup = true, withVersion = true), - ) - - path("src/main/java/my/plugin/MyPlugin.java").writeText( + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun excludeGradleApiByDefault(legacy: Boolean) { + writeGradlePluginModule(legacy) + projectScriptPath.appendText( """ - package my.plugin; - import org.gradle.api.Plugin; - import org.gradle.api.Project; - public class MyPlugin implements Plugin { - public void apply(Project project) { - System.out.println("MyPlugin: Hello World!"); - } + dependencies { + implementation 'shadow:a:1.0' + compileOnly 'shadow:b:1.0' } """.trimIndent(), ) - path("src/main/resources/META-INF/gradle-plugins/my.plugin.properties").writeText( - """ - implementation-class=my.plugin.MyPlugin - """.trimIndent(), - ) run(shadowJarTask) assertThat(outputShadowJar).useAll { transform { actual -> actual.entries().toList().map { it.name }.filter { it.endsWith(".class") } } .single().isEqualTo("my/plugin/MyPlugin.class") - transform { it.manifest.mainAttributes.keys }.all { - isNotEmpty() - // Doesn't contain Gradle classes. - doesNotContain("Class-Path") - } + transform { it.manifest.mainAttributes }.isNotEmpty() + // Doesn't contain Gradle classes. + getMainAttr("Class-Path").isNull() + + containsEntries( + "a.properties", + "a2.properties", + ) + doesNotContainEntries( + "b.properties", + ) } } From 9242747b53fe1d7a663b9f897b9dce07e719f925 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 16 Jan 2025 21:11:27 -0500 Subject: [PATCH 153/941] Tweak PublishingTest (#1162) * remoteRepoPath could be @TempDir * Reuse assertShadowJarCommon --- .../gradle/plugins/shadow/PublishingTest.kt | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index f37756ed1..836c4648b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -35,18 +35,19 @@ import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME import org.gradle.testkit.runner.BuildResult import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir class PublishingTest : BasePluginTest() { private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() private val gmmAdapter = moshi.adapter(GradleModuleMetadata::class.java) private val pomReader = MavenXpp3Reader() - private lateinit var remoteRepoPath: Path + @TempDir + lateinit var remoteRepoPath: Path @BeforeEach override fun setup() { super.setup() - remoteRepoPath = projectRoot.resolve("remote-maven-repo") settingsScriptPath.appendText("rootProject.name = 'maven'" + System.lineSeparator()) } @@ -84,16 +85,7 @@ class PublishingTest : BasePluginTest() { publish() - assertThat(repoJarPath("shadow/maven/1.0/maven-1.0.jar")).useAll { - containsEntries( - "a.properties", - "a2.properties", - ) - doesNotContainEntries( - "b.properties", - ) - getMainAttr("Class-Path").isEqualTo("b-1.0.jar") - } + assertShadowJarCommon(repoJarPath("shadow/maven/1.0/maven-1.0.jar")) assertPomCommon(repoPath("shadow/maven/1.0/maven-1.0.pom")) assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven/1.0/maven-1.0.module"))) } @@ -339,6 +331,7 @@ class PublishingTest : BasePluginTest() { doesNotContainEntries( "b.properties", ) + getMainAttr("Class-Path").isEqualTo("b-1.0.jar") } } From 4293c58e212bad51c431b2e600c7bf052639a681 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 16 Jan 2025 21:30:06 -0500 Subject: [PATCH 154/941] Add a new configuration for pluginClasspath in tests (#1163) --- build.gradle.kts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index d2f458f92..dceef9d40 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,6 +46,10 @@ spotless { } } +val testPluginClasspath: Configuration by configurations.creating { + isCanBeResolved = true +} + dependencies { implementation(libs.apache.ant) implementation(libs.apache.commonsIo) @@ -56,6 +60,8 @@ dependencies { implementation(libs.plexus.utils) implementation(libs.plexus.xml) + testPluginClasspath(libs.foojayResolver) + lintChecks(libs.androidx.gradlePluginLints) } @@ -103,8 +109,6 @@ testing.suites { implementation(libs.apache.maven.modelBuilder) implementation(libs.moshi) implementation(libs.moshi.kotlin) - // Used in test `plugins` blocks. - implementation(libs.foojayResolver) } } @@ -129,12 +133,9 @@ gradlePlugin { } tasks.pluginUnderTestMetadata { - val functionalTestImplementation = configurations.named("functionalTestImplementation") { - isCanBeResolved = true - } // Plugins used in tests could be resolved in classpath. pluginClasspath.from( - functionalTestImplementation, + testPluginClasspath, ) } From 618cd01e3c07b8005fbf8717335e1abded8e03b3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 16 Jan 2025 21:49:56 -0500 Subject: [PATCH 155/941] Try out ubuntu-24.04-arm on CI (#1164) https://github.blog/changelog/2025-01-16-linux-arm64-hosted-runners-now-available-for-free-in-public-repositories-public-preview/ --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c393c30b..38b7e36f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - os: [ ubuntu-latest, windows-latest ] + os: [ ubuntu-24.04-arm, windows-latest ] # Always test on the latest version and some LTS. java: [ 17, 21, 23 ] runs-on: ${{ matrix.os }} @@ -26,7 +26,7 @@ jobs: publish-snapshot: needs: build - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm if: github.repository == 'GradleUp/shadow' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3e9d3ca1..1ef4b7ca1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm if: github.repository == 'GradleUp/shadow' permissions: contents: write From b78c37e8d18cc0bfb432a0f97b2e2e9c157e9078 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 17 Jan 2025 03:53:07 -0500 Subject: [PATCH 156/941] Mark check depend on all tests (#1166) --- build.gradle.kts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index dceef9d40..f2346857b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -140,10 +140,7 @@ tasks.pluginUnderTestMetadata { } tasks.check { - dependsOn( - testing.suites.named("integrationTest"), - testing.suites.named("functionalTest"), - ) + dependsOn(tasks.withType()) } tasks.clean { From 66e89ce887ff4b7b9196ef7d46bfcc9589d49f52 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 17 Jan 2025 19:29:54 -0500 Subject: [PATCH 157/941] Explicit MinimizeTest (#1167) --- .../gradle/plugins/shadow/BasePluginTest.kt | 73 ----- .../gradle/plugins/shadow/JavaPluginTest.kt | 196 ------------ .../gradle/plugins/shadow/MinimizeTest.kt | 280 ++++++++++++++++++ 3 files changed, 280 insertions(+), 269 deletions(-) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt 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 bf956d417..7b9704155 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 @@ -216,79 +216,6 @@ abstract class BasePluginTest { ) } - fun writeApiLibAndImplModules() { - settingsScriptPath.appendText( - """ - include 'api', 'lib', 'impl' - """.trimIndent() + System.lineSeparator(), - ) - projectScriptPath.writeText("") - - path("lib/src/main/java/lib/LibEntity.java").writeText( - """ - package lib; - public interface LibEntity {} - """.trimIndent(), - ) - path("lib/src/main/java/lib/UnusedLibEntity.java").writeText( - """ - package lib; - public class UnusedLibEntity implements LibEntity {} - """.trimIndent(), - ) - path("lib/build.gradle").writeText( - """ - plugins { - id 'java' - } - """.trimIndent() + System.lineSeparator(), - ) - - path("api/src/main/java/api/Entity.java").writeText( - """ - package api; - public interface Entity {} - """.trimIndent(), - ) - path("api/src/main/java/api/UnusedEntity.java").writeText( - """ - package api; - import lib.LibEntity; - public class UnusedEntity implements LibEntity {} - """.trimIndent(), - ) - path("api/build.gradle").writeText( - """ - plugins { - id 'java' - } - dependencies { - implementation 'junit:junit:3.8.2' - implementation project(':lib') - } - """.trimIndent() + System.lineSeparator(), - ) - - path("impl/src/main/java/impl/SimpleEntity.java").writeText( - """ - package impl; - import api.Entity; - public class SimpleEntity implements Entity {} - """.trimIndent(), - ) - path("impl/build.gradle").writeText( - """ - ${getDefaultProjectBuildScript("java-library")} - dependencies { - api project(':api') - } - $shadowJar { - minimize() - } - """.trimIndent() + System.lineSeparator(), - ) - } - fun writeGradlePluginModule(legacy: Boolean) { val pluginId = "my.plugin" val pluginClass = "my.plugin.MyPlugin" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index a870adf68..723457fce 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -150,202 +150,6 @@ class JavaPluginTest : BasePluginTest() { } } - /** - * 'Server' depends on 'Client'. 'junit' is independent. - * The minimize shall remove 'junit'. - */ - @Test - fun minimizeByKeepingOnlyTransitiveDependencies() { - writeClientAndServerModules( - serverShadowBlock = """ - minimize() - """.trimIndent(), - ) - path("server/src/main/java/server/Server.java").writeText( - """ - package server; - import client.Client; - public class Server { - // This is to make sure that 'Client' is not removed. - private final String client = Client.class.getName(); - } - """.trimIndent(), - ) - - run(serverShadowJarTask) - - assertThat(outputServerShadowJar).useAll { - containsEntries( - "client/Client.class", - "server/Server.class", - ) - doesNotContainEntries( - "junit/framework/Test.class", - ) - } - } - - /** - * 'Client', 'Server' and 'junit' are independent. - * 'junit' is excluded from the minimize step. - * The minimize step shall remove 'Client' but not 'junit'. - */ - @Test - fun excludeDependencyFromMinimize() { - writeClientAndServerModules( - serverShadowBlock = """ - minimize { - exclude(dependency('junit:junit:.*')) - } - """.trimIndent(), - ) - - run(serverShadowJarTask) - - assertThat(outputServerShadowJar).useAll { - containsEntries( - "server/Server.class", - "junit/framework/Test.class", - ) - doesNotContainEntries( - "client/Client.class", - ) - } - } - - /** - * 'Client', 'Server' and 'junit' are independent. - * Unused classes of 'client' and theirs dependencies shouldn't be removed. - */ - @Test - fun excludeProjectFromMinimize() { - writeClientAndServerModules( - serverShadowBlock = """ - minimize { - exclude(project(':client')) - } - """.trimIndent(), - ) - - run(serverShadowJarTask) - - assertThat(outputServerShadowJar).useAll { - containsEntries( - "client/Client.class", - "server/Server.class", - ) - } - } - - /** - * 'Client', 'Server' and 'junit' are independent. - * Unused classes of 'client' and theirs dependencies shouldn't be removed. - */ - @Test - fun excludeProjectFromMinimizeShallNotExcludeTransitiveDependenciesThatAreUsedInSubproject() { - writeClientAndServerModules( - serverShadowBlock = """ - minimize { - exclude(project(':client')) - } - """.trimIndent(), - ) - path("client/src/main/java/client/Client.java").writeText( - """ - package client; - import junit.framework.TestCase; - public class Client extends TestCase { - public static void main(String[] args) {} - } - """.trimIndent(), - ) - - run(serverShadowJarTask) - - assertThat(outputServerShadowJar).useAll { - containsEntries( - "client/Client.class", - "server/Server.class", - "junit/framework/TestCase.class", - ) - } - - path("client/src/main/java/client/Client.java").writeText( - """ - package client; - public class Client {} - """.trimIndent(), - ) - run(serverShadowJarTask) - - assertThat(outputServerShadowJar).useAll { - containsEntries( - "client/Client.class", - "server/Server.class", - "junit/framework/TestCase.class", - ) - } - } - - /** - * 'api' used as api for 'impl', and depended on 'lib'. 'junit' is independent. - * The minimize step shall remove 'junit', but not 'api'. - * Unused classes of 'api' and theirs dependencies also shouldn't be removed. - */ - @Test - fun useMinimizeWithDependenciesWithApiScope() { - writeApiLibAndImplModules() - - run(":impl:$SHADOW_JAR_TASK_NAME") - - assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { - containsEntries( - "impl/SimpleEntity.class", - "api/Entity.class", - "api/UnusedEntity.class", - "lib/LibEntity.class", - ) - doesNotContainEntries( - "junit/framework/Test.class", - "lib/UnusedLibEntity.class", - ) - } - } - - /** - * 'api' used as api for 'impl', and 'lib' used as api for 'api'. - * Unused classes of 'api' and 'lib' shouldn't be removed. - */ - @Test - fun useMinimizeWithTransitiveDependenciesWithApiScope() { - writeApiLibAndImplModules() - path("api/build.gradle").writeText( - """ - plugins { - id 'java-library' - } - dependencies { - api project(':lib') - } - """.trimIndent(), - ) - - run(":impl:$SHADOW_JAR_TASK_NAME") - - assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { - containsEntries( - "impl/SimpleEntity.class", - "api/Entity.class", - "api/UnusedEntity.class", - "lib/LibEntity.class", - "lib/UnusedLibEntity.class", - ) - doesNotContainEntries( - "junit/framework/Test.class", - ) - } - } - @Test fun dependOnProjectShadowJar() { writeShadowedClientAndServerModules() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt new file mode 100644 index 000000000..d77b07180 --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -0,0 +1,280 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import kotlin.io.path.appendText +import kotlin.io.path.writeText +import org.junit.jupiter.api.Test + +class MinimizeTest : BasePluginTest() { + /** + * 'api' used as api for 'impl', and depended on 'lib'. 'junit' is independent. + * The minimize step shall remove 'junit', but not 'api'. + * Unused classes of 'api' and theirs dependencies also shouldn't be removed. + */ + @Test + fun useMinimizeWithDependenciesWithApiScope() { + writeApiLibAndImplModules() + + run(":impl:$SHADOW_JAR_TASK_NAME") + + assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { + containsEntries( + "impl/SimpleEntity.class", + "api/Entity.class", + "api/UnusedEntity.class", + "lib/LibEntity.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + "lib/UnusedLibEntity.class", + ) + } + } + + /** + * 'api' used as api for 'impl', and 'lib' used as api for 'api'. + * Unused classes of 'api' and 'lib' shouldn't be removed. + */ + @Test + fun useMinimizeWithTransitiveDependenciesWithApiScope() { + writeApiLibAndImplModules() + path("api/build.gradle").writeText( + """ + plugins { + id 'java-library' + } + dependencies { + api project(':lib') + } + """.trimIndent(), + ) + + run(":impl:$SHADOW_JAR_TASK_NAME") + + assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { + containsEntries( + "impl/SimpleEntity.class", + "api/Entity.class", + "api/UnusedEntity.class", + "lib/LibEntity.class", + "lib/UnusedLibEntity.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } + } + + /** + * 'Server' depends on 'Client'. 'junit' is independent. + * The minimize shall remove 'junit'. + */ + @Test + fun minimizeByKeepingOnlyTransitiveDependencies() { + writeClientAndServerModules( + serverShadowBlock = """ + minimize() + """.trimIndent(), + ) + path("server/src/main/java/server/Server.java").writeText( + """ + package server; + import client.Client; + public class Server { + // This is to make sure that 'Client' is not removed. + private final String client = Client.class.getName(); + } + """.trimIndent(), + ) + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } + } + + /** + * 'Client', 'Server' and 'junit' are independent. + * 'junit' is excluded from the minimize step. + * The minimize step shall remove 'Client' but not 'junit'. + */ + @Test + fun excludeDependencyFromMinimize() { + writeClientAndServerModules( + serverShadowBlock = """ + minimize { + exclude(dependency('junit:junit:.*')) + } + """.trimIndent(), + ) + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).useAll { + containsEntries( + "server/Server.class", + "junit/framework/Test.class", + ) + doesNotContainEntries( + "client/Client.class", + ) + } + } + + /** + * 'Client', 'Server' and 'junit' are independent. + * Unused classes of 'client' and theirs dependencies shouldn't be removed. + */ + @Test + fun excludeProjectFromMinimize() { + writeClientAndServerModules( + serverShadowBlock = """ + minimize { + exclude(project(':client')) + } + """.trimIndent(), + ) + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + ) + } + } + + /** + * 'Client', 'Server' and 'junit' are independent. + * Unused classes of 'client' and theirs dependencies shouldn't be removed. + */ + @Test + fun excludeProjectFromMinimizeShallNotExcludeTransitiveDependenciesThatAreUsedInSubproject() { + writeClientAndServerModules( + serverShadowBlock = """ + minimize { + exclude(project(':client')) + } + """.trimIndent(), + ) + path("client/src/main/java/client/Client.java").writeText( + """ + package client; + import junit.framework.TestCase; + public class Client extends TestCase { + public static void main(String[] args) {} + } + """.trimIndent(), + ) + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + "junit/framework/TestCase.class", + ) + } + + path("client/src/main/java/client/Client.java").writeText( + """ + package client; + public class Client {} + """.trimIndent(), + ) + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).useAll { + containsEntries( + "client/Client.class", + "server/Server.class", + "junit/framework/TestCase.class", + ) + } + } + + private fun writeApiLibAndImplModules() { + settingsScriptPath.appendText( + """ + include 'api', 'lib', 'impl' + """.trimIndent() + System.lineSeparator(), + ) + projectScriptPath.writeText("") + + path("lib/src/main/java/lib/LibEntity.java").writeText( + """ + package lib; + public interface LibEntity {} + """.trimIndent(), + ) + path("lib/src/main/java/lib/UnusedLibEntity.java").writeText( + """ + package lib; + public class UnusedLibEntity implements LibEntity {} + """.trimIndent(), + ) + path("lib/build.gradle").writeText( + """ + plugins { + id 'java' + } + """.trimIndent() + System.lineSeparator(), + ) + + path("api/src/main/java/api/Entity.java").writeText( + """ + package api; + public interface Entity {} + """.trimIndent(), + ) + path("api/src/main/java/api/UnusedEntity.java").writeText( + """ + package api; + import lib.LibEntity; + public class UnusedEntity implements LibEntity {} + """.trimIndent(), + ) + path("api/build.gradle").writeText( + """ + plugins { + id 'java' + } + dependencies { + implementation 'junit:junit:3.8.2' + implementation project(':lib') + } + """.trimIndent() + System.lineSeparator(), + ) + + path("impl/src/main/java/impl/SimpleEntity.java").writeText( + """ + package impl; + import api.Entity; + public class SimpleEntity implements Entity {} + """.trimIndent(), + ) + path("impl/build.gradle").writeText( + """ + ${getDefaultProjectBuildScript("java-library")} + dependencies { + api project(':api') + } + $shadowJar { + minimize() + } + """.trimIndent() + System.lineSeparator(), + ) + } +} From 1845e43127f195cee3be48df8055b32aa2a413ce Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 18 Jan 2025 00:11:58 -0500 Subject: [PATCH 158/941] Test publishing shadowed Gradle plugin (#1156) * Add publishShadowedGradlePluginWithMavenPublishPlugin * Disable CC for publish task to fix the flaky results * Revert "Disable CC for publish task to fix the flaky results" This reverts commit b49478ce3362b7fe92327fbc61690ce2c8772e52. * Disable automatedPublishing * Revert "Disable automatedPublishing" This reverts commit 23749432d2165f5d42a6e317299e4b0fdd56e345. * Add plugin-publish-with-shadow project for testing * Revert "Add plugin-publish-with-shadow project for testing" This reverts commit eb90843ff4ed6235c70f1fa4356d582bf116164c. * Apply com.gradle.plugin-publish and fix flaky tests * Tweak checks * Note com.gradle.plugin-publish in plugin publication doc * Cleanups --- build.gradle.kts | 1 + src/docs/plugins/README.md | 2 +- .../gradle/plugins/shadow/BasePluginTest.kt | 2 +- .../gradle/plugins/shadow/PublishingTest.kt | 44 +++++++++++++++++-- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f2346857b..20f9bdff9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -61,6 +61,7 @@ dependencies { implementation(libs.plexus.xml) testPluginClasspath(libs.foojayResolver) + testPluginClasspath(libs.pluginPublish) lintChecks(libs.androidx.gradlePluginLints) } diff --git a/src/docs/plugins/README.md b/src/docs/plugins/README.md index d0e107bf4..afc771dc5 100644 --- a/src/docs/plugins/README.md +++ b/src/docs/plugins/README.md @@ -12,7 +12,7 @@ A simple Gradle plugin can use this feature by applying the `shadow` plugin and ```groovy plugins { - id 'java-gradle-plugin' + id 'java-gradle-plugin' // May have to apply the latest `com.gradle.plugin-publish` for better publishing support. id 'com.gradleup.shadow' } 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 7b9704155..088095ef0 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 @@ -229,7 +229,7 @@ abstract class BasePluginTest { gradlePluginBlock = """ gradlePlugin { plugins { - create("myPlugin") { + create('myPlugin') { id = '$pluginId' implementationClass = '$pluginClass' } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 836c4648b..37a9fc37f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -6,7 +6,6 @@ import assertk.assertions.contains import assertk.assertions.containsOnly import assertk.assertions.isEmpty import assertk.assertions.isEqualTo -import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata import com.github.jengelman.gradle.plugins.shadow.util.Issue @@ -21,7 +20,8 @@ import java.nio.file.Path import kotlin.io.path.appendText import kotlin.io.path.exists import kotlin.io.path.inputStream -import kotlin.io.path.isRegularFile +import kotlin.io.path.listDirectoryEntries +import kotlin.io.path.name import kotlin.io.path.readText import kotlin.io.path.writeText import org.apache.maven.model.Model @@ -36,6 +36,8 @@ import org.gradle.testkit.runner.BuildResult import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class PublishingTest : BasePluginTest() { private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() @@ -90,6 +92,43 @@ class PublishingTest : BasePluginTest() { assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven/1.0/maven-1.0.module"))) } + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun publishShadowedGradlePluginWithMavenPublishPlugin(legacy: Boolean) { + writeGradlePluginModule(legacy) + projectScriptPath.appendText( + publishConfiguration( + projectBlock = """ + apply plugin: 'com.gradle.plugin-publish' + group = 'my.plugin' + version = '1.0' + """.trimIndent(), + shadowBlock = """ + archiveClassifier = '' + """.trimIndent(), + publicationsBlock = """ + pluginMaven(MavenPublication) { + artifactId = 'my-gradle-plugin' + } + """.trimIndent(), + ), + ) + + publish() + + val artifactRoot = "my/plugin/my-gradle-plugin/1.0" + assertThat(repoPath(artifactRoot).listDirectoryEntries("*.jar").map(Path::name)).containsOnly( + "my-gradle-plugin-1.0.jar", + "my-gradle-plugin-1.0-javadoc.jar", + "my-gradle-plugin-1.0-sources.jar", + ) + + val artifactPrefix = "$artifactRoot/my-gradle-plugin-1.0" + assertShadowJarCommon(repoJarPath("$artifactPrefix.jar")) + assertPomCommon(repoPath("$artifactPrefix.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("$artifactPrefix.module"))) + } + @Issue( "https://github.com/GradleUp/shadow/issues/860", "https://github.com/GradleUp/shadow/issues/945", @@ -256,7 +295,6 @@ class PublishingTest : BasePluginTest() { private fun repoPath(relative: String): Path { return remoteRepoPath.resolve(relative).also { check(it.exists()) { "Path not found: $it" } - check(it.isRegularFile()) { "Path is not a regular file: $it" } } } From 8300f91df0114f5cb240f754ef082ea4150b3456 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 18 Jan 2025 06:19:31 -0500 Subject: [PATCH 159/941] Support configuring separator in AppendingTransformer (#1169) --- api/shadow.api | 9 ++ src/docs/changes/README.md | 2 + src/docs/configuration/merging/README.md | 16 +++- .../transformers/AppendingTransformerTest.kt | 86 ++++++++++++++++++- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 14 +++ .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 2 + .../transformers/AppendingTransformer.kt | 14 ++- 7 files changed, 136 insertions(+), 7 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index f25735c69..ae77e4ad5 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -297,6 +297,8 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun ()V public fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun append (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun append (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; protected fun copy ()V protected fun createCopyAction ()Lorg/gradle/api/internal/file/copy/CopyAction; public fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; @@ -349,6 +351,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec : org/gradle/api/file/CopySpec { public abstract fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun append (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; public abstract fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; @@ -397,15 +400,21 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNotic } public class com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer$Companion; + public static final field DEFAULT_SEPARATOR Ljava/lang/String; public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun getResource ()Lorg/gradle/api/provider/Property; + public fun getSeparator ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } +public final class com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer$Companion { +} + public abstract interface annotation class com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer : java/lang/annotation/Annotation { } diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index bf38070dd..2ecd59b23 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -12,6 +12,8 @@ - **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) Some public `List` parameters are also changed to `Set`. - Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) +- Support configuring `separator` in `AppendingTransformer`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) + This is useful for handling files like `resources/application.yml`. **Fixed** diff --git a/src/docs/configuration/merging/README.md b/src/docs/configuration/merging/README.md index 9c9fbcc42..1e2c7d8ba 100644 --- a/src/docs/configuration/merging/README.md +++ b/src/docs/configuration/merging/README.md @@ -180,7 +180,7 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow Generic text files can be appended together using the [`AppendingTransformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.html). -Each file is appended using new lines to separate content. +Each file is appended using separators (defaults to `\n`) to separate content. The [`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html) task provides a short syntax method of [`append(String)`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html#append(java.lang.String)) to @@ -193,6 +193,20 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow } ``` +```groovy +// Appending application.yml files +tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // short syntax + append('resources/application.yml', '\n---\n') + // full syntax + transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer.class) { + resource = 'resources/custom-config/application.yml' + separator = '\n---\n' + } +} +``` + + ## Appending XML Files XML files require a special transformer for merging. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index a32a8aa9b..a883faf8a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -3,12 +3,13 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.util.getContent +import java.nio.file.Path import kotlin.io.path.appendText import org.junit.jupiter.api.Test class AppendingTransformerTest : BaseTransformerTest() { @Test - fun appendingTransformer() { + fun appendTestProperties() { val one = buildJarOne { insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) } @@ -26,12 +27,12 @@ class AppendingTransformerTest : BaseTransformerTest() { run(shadowJarTask) - val content = outputShadowJar.use { it.getContent(ENTRY_TEST_PROPERTIES) }.trimIndent() + val content = outputShadowJar.use { it.getContent(ENTRY_TEST_PROPERTIES) } assertThat(content).isEqualTo(CONTENT_ONE_TWO) } @Test - fun appendingTransformerShortSyntax() { + fun appendTestPropertiesShortSyntax() { val one = buildJarOne { insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) } @@ -49,7 +50,84 @@ class AppendingTransformerTest : BaseTransformerTest() { run(shadowJarTask) - val content = outputShadowJar.use { it.getContent(ENTRY_TEST_PROPERTIES) }.trimIndent() + val content = outputShadowJar.use { it.getContent(ENTRY_TEST_PROPERTIES) } assertThat(content).isEqualTo(CONTENT_ONE_TWO) } + + @Test + fun appendApplicationYaml() { + val (one, two) = writeApplicationYamlJars() + + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar(one, two), + transformerBlock = """ + resource = 'resources/$APPLICATION_YML_FILE' + separator = '$APPLICATION_YML_SEPARATOR' + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + val content = outputShadowJar.use { it.getContent("resources/$APPLICATION_YML_FILE") } + assertThat(content).isEqualTo( + """ + $CONTENT_ONE + --- + $CONTENT_TWO + """.trimIndent(), + ) + } + + @Test + fun appendApplicationYamlShortSyntax() { + val (one, two) = writeApplicationYamlJars() + + projectScriptPath.appendText( + """ + $shadowJar { + ${fromJar(one, two)} + append('resources/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') + append('resources/config/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') + } + """.trimIndent(), + ) + + run(shadowJarTask) + + val content1 = outputShadowJar.use { it.getContent("resources/$APPLICATION_YML_FILE") } + assertThat(content1).isEqualTo( + """ + $CONTENT_ONE + --- + $CONTENT_TWO + """.trimIndent(), + ) + val content2 = outputShadowJar.use { it.getContent("resources/config/$APPLICATION_YML_FILE") } + assertThat(content2).isEqualTo( + """ + $CONTENT_TWO + --- + $CONTENT_THREE + """.trimIndent(), + ) + } + + private fun writeApplicationYamlJars(): Pair { + val one = buildJarOne { + insert("resources/$APPLICATION_YML_FILE", CONTENT_ONE) + insert("resources/config/$APPLICATION_YML_FILE", CONTENT_TWO) + } + val two = buildJarTwo { + insert("resources/$APPLICATION_YML_FILE", CONTENT_TWO) + insert("resources/config/$APPLICATION_YML_FILE", CONTENT_THREE) + } + return one to two + } + + private companion object { + const val APPLICATION_YML_FILE = "application.yml" + const val APPLICATION_YML_SEPARATOR = "\\n---\\n" + } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 2ef97d17a..0c00f4206 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -209,9 +209,23 @@ public abstract class ShadowJar : } override fun append(resourcePath: String): ShadowJar { + return append(resourcePath, AppendingTransformer.DEFAULT_SEPARATOR) + } + + /** + * Append contents to a resource in the jar. + * + * e.g. `append("resources/application.yml", "\n---\n")` for merging `resources/application.yml` files. + * + * @param resourcePath The path to the resource in the jar. + * @param separator The separator to use between the original content and the appended content, + * defaults to `\n` ([AppendingTransformer.DEFAULT_SEPARATOR]). + */ + override fun append(resourcePath: String, separator: String): ShadowJar { return runCatching { transform(AppendingTransformer::class.java) { it.resource.set(resourcePath) + it.separator.set(separator) } }.getOrDefault(this) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt index f51bf1716..8f360f642 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -45,6 +45,8 @@ public interface ShadowSpec : CopySpec { public fun append(resourcePath: String): ShadowSpec + public fun append(resourcePath: String, separator: String): ShadowSpec + public fun relocate(pattern: String, destination: String): ShadowSpec public fun relocate(pattern: String, destination: String, action: Action?): ShadowSpec diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index 182d12f33..7b3dd768b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -27,20 +27,26 @@ public open class AppendingTransformer @Inject constructor( * Defer initialization, see [issue 763](https://github.com/GradleUp/shadow/issues/763). */ private var _data: ByteArrayOutputStream? = null - private inline val data get() = if (_data == null) ByteArrayOutputStream().also { _data = it } else _data!! + private inline val data get() = _data ?: ByteArrayOutputStream().also { _data = it } @get:Optional @get:Input public open val resource: Property = objectFactory.property() + @get:Input + public open val separator: Property = objectFactory.property(DEFAULT_SEPARATOR) + override fun canTransformResource(element: FileTreeElement): Boolean { return resource.orNull.equals(element.relativePath.pathString, ignoreCase = true) } override fun transform(context: TransformerContext) { context.inputStream.use { + if (data.size() > 0) { + // Append the separator before the new content to ensure the separator is not at the end of the file. + data.write(separator.get().toByteArray()) + } it.copyTo(data) - data.write('\n'.code) } } @@ -57,4 +63,8 @@ public open class AppendingTransformer @Inject constructor( data.toByteArray().inputStream().copyTo(os) data.reset() } + + public companion object { + public const val DEFAULT_SEPARATOR: String = "\n" + } } From aa1fde3a3af3ad324c81f0587bfde4744ff35362 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 18 Jan 2025 06:55:21 -0500 Subject: [PATCH 160/941] Merge duplicates using parameterized tests (#1170) * Rename serviceResourceTransformerShortSyntaxRelocation * Merge short syntax tests with normal ones --- .../transformers/AppendingTransformerTest.kt | 107 +++++++----------- .../GroovyExtensionModuleTransformerTest.kt | 37 +++--- .../ServiceFileTransformerTest.kt | 88 ++++++-------- 3 files changed, 89 insertions(+), 143 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index a883faf8a..7a525bb8f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -3,27 +3,36 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.util.getContent -import java.nio.file.Path import kotlin.io.path.appendText -import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class AppendingTransformerTest : BaseTransformerTest() { - @Test - fun appendTestProperties() { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun appendTestProperties(shortSyntax: Boolean) { val one = buildJarOne { insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) } val two = buildJarTwo { insert(ENTRY_TEST_PROPERTIES, CONTENT_TWO) } - projectScriptPath.appendText( + val config = if (shortSyntax) { + """ + $shadowJar { + ${fromJar(one, two)} + append('$ENTRY_TEST_PROPERTIES') + } + """.trimIndent() + } else { transform( shadowJarBlock = fromJar(one, two), transformerBlock = """ resource = '$ENTRY_TEST_PROPERTIES' """.trimIndent(), - ), - ) + ) + } + projectScriptPath.appendText(config) run(shadowJarTask) @@ -31,68 +40,44 @@ class AppendingTransformerTest : BaseTransformerTest() { assertThat(content).isEqualTo(CONTENT_ONE_TWO) } - @Test - fun appendTestPropertiesShortSyntax() { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun appendApplicationYaml(shortSyntax: Boolean) { val one = buildJarOne { - insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) + insert("resources/$APPLICATION_YML_FILE", CONTENT_ONE) + insert("resources/config/$APPLICATION_YML_FILE", CONTENT_TWO) } val two = buildJarTwo { - insert(ENTRY_TEST_PROPERTIES, CONTENT_TWO) + insert("resources/$APPLICATION_YML_FILE", CONTENT_TWO) + insert("resources/config/$APPLICATION_YML_FILE", CONTENT_THREE) } - projectScriptPath.appendText( + val config = if (shortSyntax) { """ $shadowJar { ${fromJar(one, two)} - append('$ENTRY_TEST_PROPERTIES') + append('resources/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') + append('resources/config/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') } - """.trimIndent(), - ) - - run(shadowJarTask) - - val content = outputShadowJar.use { it.getContent(ENTRY_TEST_PROPERTIES) } - assertThat(content).isEqualTo(CONTENT_ONE_TWO) - } - - @Test - fun appendApplicationYaml() { - val (one, two) = writeApplicationYamlJars() - - projectScriptPath.appendText( - transform( + """.trimIndent() + } else { + val block1 = transform( shadowJarBlock = fromJar(one, two), transformerBlock = """ resource = 'resources/$APPLICATION_YML_FILE' separator = '$APPLICATION_YML_SEPARATOR' """.trimIndent(), - ), - ) - - run(shadowJarTask) - - val content = outputShadowJar.use { it.getContent("resources/$APPLICATION_YML_FILE") } - assertThat(content).isEqualTo( - """ - $CONTENT_ONE - --- - $CONTENT_TWO - """.trimIndent(), - ) - } + ) + val block2 = transform( + shadowJarBlock = fromJar(one, two), + transformerBlock = """ + resource = 'resources/config/$APPLICATION_YML_FILE' + separator = '$APPLICATION_YML_SEPARATOR' + """.trimIndent(), + ) + block1 + System.lineSeparator() + block2 + } - @Test - fun appendApplicationYamlShortSyntax() { - val (one, two) = writeApplicationYamlJars() - - projectScriptPath.appendText( - """ - $shadowJar { - ${fromJar(one, two)} - append('resources/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') - append('resources/config/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') - } - """.trimIndent(), - ) + projectScriptPath.appendText(config) run(shadowJarTask) @@ -114,18 +99,6 @@ class AppendingTransformerTest : BaseTransformerTest() { ) } - private fun writeApplicationYamlJars(): Pair { - val one = buildJarOne { - insert("resources/$APPLICATION_YML_FILE", CONTENT_ONE) - insert("resources/config/$APPLICATION_YML_FILE", CONTENT_TWO) - } - val two = buildJarTwo { - insert("resources/$APPLICATION_YML_FILE", CONTENT_TWO) - insert("resources/config/$APPLICATION_YML_FILE", CONTENT_THREE) - } - return one to two - } - private companion object { const val APPLICATION_YML_FILE = "application.yml" const val APPLICATION_YML_SEPARATOR = "\\n---\\n" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index a13a43dbb..ecb4a6991 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -14,15 +14,26 @@ import com.github.jengelman.gradle.plugins.shadow.util.getContent import java.nio.file.Path import kotlin.io.path.appendText import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { - @Test - fun groovyExtensionModuleTransformer() { - projectScriptPath.appendText( + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun groovyExtensionModuleTransformer(shortSyntax: Boolean) { + val config = if (shortSyntax) { + """ + $shadowJar { + ${fromJar(buildJarFoo(), buildJarBar())} + mergeGroovyExtensionModules() + } + """.trimIndent() + } else { transform( shadowJarBlock = fromJar(buildJarFoo(), buildJarBar()), - ), - ) + ) + } + projectScriptPath.appendText(config) run(shadowJarTask) @@ -45,22 +56,6 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { commonAssertions(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR) } - @Test - fun groovyExtensionModuleTransformerShortSyntax() { - projectScriptPath.appendText( - """ - $shadowJar { - ${fromJar(buildJarFoo(), buildJarBar())} - mergeGroovyExtensionModules() - } - """.trimIndent(), - ) - - run(shadowJarTask) - - commonAssertions(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) - } - private fun buildJarFoo( entry: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, ): Path = buildJar("foo.jar") { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 8fef8b325..684f95bbf 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -7,18 +7,31 @@ import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class ServiceFileTransformerTest : BaseTransformerTest() { - @Test - fun serviceResourceTransformer() { - projectScriptPath.appendText( + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun serviceResourceTransformer(shortSyntax: Boolean) { + val config = if (shortSyntax) { + """ + $shadowJar { + ${fromJar(buildJarOne(), buildJarTwo())} + mergeServiceFiles { + exclude 'META-INF/services/com.acme.*' + } + } + """.trimIndent() + } else { transform( shadowJarBlock = fromJar(buildJarOne(), buildJarTwo()), transformerBlock = """ exclude 'META-INF/services/com.acme.*' """.trimIndent(), - ), - ) + ) + } + projectScriptPath.appendText(config) run(shadowJarTask) @@ -28,22 +41,31 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } - @Test - fun serviceResourceTransformerAlternatePath() { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun serviceResourceTransformerAlternatePath(shortSyntax: Boolean) { val one = buildJarOne { insert(ENTRY_FOO_SHADE, CONTENT_ONE) } val two = buildJarTwo { insert(ENTRY_FOO_SHADE, CONTENT_TWO) } - projectScriptPath.appendText( + val config = if (shortSyntax) { + """ + $shadowJar { + ${fromJar(one, two)} + mergeServiceFiles("META-INF/foo") + } + """.trimIndent() + } else { transform( shadowJarBlock = fromJar(one, two), transformerBlock = """ path = 'META-INF/foo' """.trimIndent(), - ), - ) + ) + } + projectScriptPath.appendText(config) run(shadowJarTask) @@ -52,28 +74,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } @Test - fun serviceResourceTransformerShortSyntax() { - projectScriptPath.appendText( - """ - $shadowJar { - ${fromJar(buildJarOne(), buildJarTwo())} - mergeServiceFiles { - exclude 'META-INF/services/com.acme.*' - } - } - """.trimIndent(), - ) - - run(shadowJarTask) - - assertThat(outputShadowJar).useAll { - getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) - getContent(ENTRY_SERVICES_FOO).isEqualTo("one") - } - } - - @Test - fun serviceResourceTransformerShortSyntaxRelocation() { + fun serviceResourceTransformerRelocation() { val one = buildJarOne { insert( "META-INF/services/java.sql.Driver", @@ -148,29 +149,6 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } - @Test - fun serviceResourceTransformerShortSyntaxAlternatePath() { - val one = buildJarOne { - insert(ENTRY_FOO_SHADE, CONTENT_ONE) - } - val two = buildJarTwo { - insert(ENTRY_FOO_SHADE, CONTENT_TWO) - } - projectScriptPath.appendText( - """ - $shadowJar { - ${fromJar(one, two)} - mergeServiceFiles("META-INF/foo") - } - """.trimIndent(), - ) - - run(shadowJarTask) - - val content = outputShadowJar.use { it.getContent(ENTRY_FOO_SHADE) } - assertThat(content).isEqualTo(CONTENT_ONE_TWO) - } - @Issue( "https://github.com/GradleUp/shadow/issues/70", "https://github.com/GradleUp/shadow/issues/71", From ad448a533947fa86165a929f58d6585edce94964 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 18 Jan 2025 07:07:40 -0500 Subject: [PATCH 161/941] Add BooleanParameterizedTest (#1171) * Replace ValueSource for booleans with BooleanValueSource * Combine ParameterizedTest with ValueSource --- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 6 ++---- .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 6 ++---- .../shadow/transformers/AppendingTransformerTest.kt | 9 +++------ .../GroovyExtensionModuleTransformerTest.kt | 6 ++---- .../shadow/transformers/ServiceFileTransformerTest.kt | 9 +++------ .../plugins/shadow/util/BooleanParameterizedTest.kt | 10 ++++++++++ 6 files changed, 22 insertions(+), 24 deletions(-) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/BooleanParameterizedTest.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 723457fce..943754bc6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -12,6 +12,7 @@ import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries @@ -28,8 +29,6 @@ import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledForJreRange import org.junit.jupiter.api.condition.JRE -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource class JavaPluginTest : BasePluginTest() { @Test @@ -448,8 +447,7 @@ class JavaPluginTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/459", "https://github.com/GradleUp/shadow/issues/852", ) - @ParameterizedTest - @ValueSource(booleans = [false, true]) + @BooleanParameterizedTest fun excludeGradleApiByDefault(legacy: Boolean) { writeGradlePluginModule(legacy) projectScriptPath.appendText( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 37a9fc37f..7b60d8233 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -7,6 +7,7 @@ import assertk.assertions.containsOnly import assertk.assertions.isEmpty import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME +import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath @@ -36,8 +37,6 @@ import org.gradle.testkit.runner.BuildResult import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource class PublishingTest : BasePluginTest() { private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() @@ -92,8 +91,7 @@ class PublishingTest : BasePluginTest() { assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven/1.0/maven-1.0.module"))) } - @ParameterizedTest - @ValueSource(booleans = [false, true]) + @BooleanParameterizedTest fun publishShadowedGradlePluginWithMavenPublishPlugin(legacy: Boolean) { writeGradlePluginModule(legacy) projectScriptPath.appendText( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 7a525bb8f..496f99a2e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -2,14 +2,12 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource class AppendingTransformerTest : BaseTransformerTest() { - @ParameterizedTest - @ValueSource(booleans = [false, true]) + @BooleanParameterizedTest fun appendTestProperties(shortSyntax: Boolean) { val one = buildJarOne { insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) @@ -40,8 +38,7 @@ class AppendingTransformerTest : BaseTransformerTest() { assertThat(content).isEqualTo(CONTENT_ONE_TWO) } - @ParameterizedTest - @ValueSource(booleans = [false, true]) + @BooleanParameterizedTest fun appendApplicationYaml(shortSyntax: Boolean) { val one = buildJarOne { insert("resources/$APPLICATION_YML_FILE", CONTENT_ONE) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index ecb4a6991..8a45b1cea 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -10,16 +10,14 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_VERSION import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR +import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.getContent import java.nio.file.Path import kotlin.io.path.appendText import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { - @ParameterizedTest - @ValueSource(booleans = [false, true]) + @BooleanParameterizedTest fun groovyExtensionModuleTransformer(shortSyntax: Boolean) { val config = if (shortSyntax) { """ diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 684f95bbf..85ff7e13d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -2,17 +2,15 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource class ServiceFileTransformerTest : BaseTransformerTest() { - @ParameterizedTest - @ValueSource(booleans = [false, true]) + @BooleanParameterizedTest fun serviceResourceTransformer(shortSyntax: Boolean) { val config = if (shortSyntax) { """ @@ -41,8 +39,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } - @ParameterizedTest - @ValueSource(booleans = [false, true]) + @BooleanParameterizedTest fun serviceResourceTransformerAlternatePath(shortSyntax: Boolean) { val one = buildJarOne { insert(ENTRY_FOO_SHADE, CONTENT_ONE) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/BooleanParameterizedTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/BooleanParameterizedTest.kt new file mode 100644 index 000000000..ba3d474f0 --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/BooleanParameterizedTest.kt @@ -0,0 +1,10 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) +@Retention(AnnotationRetention.RUNTIME) +@ParameterizedTest +@ValueSource(booleans = [false, true]) +annotation class BooleanParameterizedTest From 193e9981505c904d7685cb3efaaaa93b94ff652c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 18 Jan 2025 09:09:59 -0500 Subject: [PATCH 162/941] Don't replace snapshot-version (#1173) --- .../src/main/kotlin/shadow.convention.deploy.gradle.kts | 1 - src/docs/getting-started/README.md | 7 +++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts index b9ea53708..60cfb44fc 100644 --- a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts @@ -24,7 +24,6 @@ gitPublish { filter( "tokens" to mapOf( "version" to version, - "snapshot-version" to "$version-SNAPSHOT", ), ) } diff --git a/src/docs/getting-started/README.md b/src/docs/getting-started/README.md index ae53b93dc..ddfdebddb 100644 --- a/src/docs/getting-started/README.md +++ b/src/docs/getting-started/README.md @@ -25,7 +25,10 @@ apply plugin: 'com.gradleup.shadow' ```
-Snapshots of the development version are available in Sonatype's snapshots repository. +Snapshots of the development version are available in + +Sonatype's snapshots repository. +

```groovy no-run @@ -35,7 +38,7 @@ buildscript { maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } } dependencies { - classpath 'com.gradleup.shadow:shadow-gradle-plugin:@snapshot-version@' + classpath 'com.gradleup.shadow:shadow-gradle-plugin:' } } From 7a94d891a70c2aba67e14064c42193a8df5f3b88 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 18 Jan 2025 09:43:35 -0500 Subject: [PATCH 163/941] Replace API links (#1174) --- src/docs/.vuepress/components/ApiLink.vue | 4 +-- src/docs/configuration/README.md | 4 +-- src/docs/configuration/dependencies/README.md | 10 +++--- src/docs/configuration/merging/README.md | 34 +++++++++---------- src/docs/custom-tasks/README.md | 2 +- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/docs/.vuepress/components/ApiLink.vue b/src/docs/.vuepress/components/ApiLink.vue index 48b5cd3fc..0eee4ead7 100644 --- a/src/docs/.vuepress/components/ApiLink.vue +++ b/src/docs/.vuepress/components/ApiLink.vue @@ -6,8 +6,8 @@ export default { data () { return { - absolutePath: "https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/" + this.path + absolutePath: "https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow/" + this.path } } } - \ No newline at end of file + diff --git a/src/docs/configuration/README.md b/src/docs/configuration/README.md index 8d2213526..94660b90a 100644 --- a/src/docs/configuration/README.md +++ b/src/docs/configuration/README.md @@ -4,10 +4,10 @@ api: api/com/github/jengelman/gradle/plugins/shadow # Configuring Shadow -The [`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html) task type extends from Gradle's +The [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type extends from Gradle's [`Jar`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) type. This means that all attributes and methods available on `Jar` are also available on -[`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html). +[`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html). Refer the _Gradle User Guide_ for [Jar](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) for details. diff --git a/src/docs/configuration/dependencies/README.md b/src/docs/configuration/dependencies/README.md index b02af528f..fa3fcf6e8 100644 --- a/src/docs/configuration/dependencies/README.md +++ b/src/docs/configuration/dependencies/README.md @@ -3,7 +3,7 @@ Shadow configures the default `shadowJar` task to merge all dependencies from the project's `runtimeClasspath` configuration into the final JAR. The configurations from which to source dependencies for the merging can be configured using the `configurations` property -of the [`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html) task type. +of the [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type. ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { @@ -15,10 +15,10 @@ The above code sample would configure the `shadowJar` task to merge dependencies This means any dependency declared in the `runtimeOnly` configuration would be **not** be included in the final JAR. > Note the literal use of `project.configurations` when setting the `configurations` attribute of a -[`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html) task. +[`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. This is **required**. It maybe be tempting to specify `configurations = [configurations.compile]` but this will not have the intended effect, as `configurations.compile` will try to delegate to the `configurations` property of the -the [`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html) task instead of the `project` +the [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task instead of the `project` ## Embedding Jar Files Inside Your Shadow Jar @@ -31,7 +31,7 @@ begins. ## Filtering Dependencies Individual dependencies can be filtered from the final JAR by using the `dependencies` block of a -[`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html) task. +[`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. Dependency filtering does **not** apply to transitive dependencies. That is, excluding a dependency does not exclude any of its dependencies from the final JAR. @@ -135,7 +135,7 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow ### Programmatically Selecting Dependencies to Filter If more complex decisions are needed to select the dependencies to be included, the -[`dependencies`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html#dependencies(Action)) +[`dependencies`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html) block provides a method that accepts a `Closure` for selecting dependencies. ```groovy diff --git a/src/docs/configuration/merging/README.md b/src/docs/configuration/merging/README.md index 1e2c7d8ba..5c224fa91 100644 --- a/src/docs/configuration/merging/README.md +++ b/src/docs/configuration/merging/README.md @@ -1,11 +1,11 @@ # Controlling JAR Content Merging Shadow allows for customizing the process by which the output JAR is generated through the -[`Transformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.html) interface. +[`Transformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-transformer/index.html) interface. This is a concept that has been carried over from the original Maven Shade implementation. -A [`Transformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.html) is invoked for each +A [`Transformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-transformer/index.html) is invoked for each entry in the JAR before being written to the final output JAR. -This allows a [`Transformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.html) to +This allows a [`Transformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-transformer/index.html) to determine if it should process a particular entry and apply any modifications before writing the stream to the output. ```groovy @@ -111,7 +111,7 @@ At runtime, this file is read and used to configure library or application behav Multiple dependencies may use the same service descriptor file name. In this case, it is generally desired to merge the content of each instance of the file into a single output file. -The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.html) +The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) class is used to perform this merging. By default, it will merge each copy of a file under `META-INF/services` into a single file in the output JAR. @@ -123,17 +123,17 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow ``` The above code snippet is a convenience syntax for calling -[`transform(ServiceFileTransformer.class)`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html#transform(Class)). +[`transform(ServiceFileTransformer.class)`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html). > Groovy Extension Module descriptor files (located at `META-INF/services/org.codehaus.groovy.runtime.ExtensionModule`) -are ignored by the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.html). +are ignored by the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html). This is due to these files having a different syntax than standard service descriptor files. -Use the [`mergeGroovyExtensionModules()`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html#mergeGroovyExtensionModules()) method to merge +Use the [`mergeGroovyExtensionModules()`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/merge-groovy-extension-modules.html) method to merge these files if your dependencies contain them. ### Configuring the Location of Service Descriptor Files -By default the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.html) +By default the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) is configured to merge files in `META-INF/services`. This directory can be overridden to merge descriptor files in a different location. @@ -148,7 +148,7 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow #### Excluding/Including Specific Service Descriptor Files From Merging -The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.html) +The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) class supports specifying specific files to include or exclude from merging. ```groovy @@ -164,9 +164,9 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow Shadow provides a specific transformer for dealing with Groovy extension module files. This is due to their special syntax and how they need to be merged together. -The [`GroovyExtensionModuleTransformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.html) +The [`GroovyExtensionModuleTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-groovy-extension-module-transformer/index.html) will handle these files. -The [`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html) task also provides a short syntax +The [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task also provides a short syntax method to add this transformer. ```groovy @@ -179,11 +179,11 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow ## Appending Text Files Generic text files can be appended together using the -[`AppendingTransformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.html). +[`AppendingTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html). Each file is appended using separators (defaults to `\n`) to separate content. -The [`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html) task provides a short syntax +The [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task provides a short syntax method of -[`append(String)`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html#append(java.lang.String)) to +[`append(String)`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html) to configure this transformer. ```groovy @@ -210,10 +210,10 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow ## Appending XML Files XML files require a special transformer for merging. -The [`XmlAppendingTransformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.html) +The [`XmlAppendingTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html) reads each XML document and merges each root element into a single document. -There is no short syntax method for the [`XmlAppendingTransformer`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.html). -It must be added using the [`transform`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow//tasks/ShadowJar.html#transform(Class)) methods. +There is no short syntax method for the [`XmlAppendingTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html). +It must be added using the [`transform`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html)) methods. ```groovy // Appending a XML File diff --git a/src/docs/custom-tasks/README.md b/src/docs/custom-tasks/README.md index ccf85a5e5..23559d03c 100644 --- a/src/docs/custom-tasks/README.md +++ b/src/docs/custom-tasks/README.md @@ -1,7 +1,7 @@ # Creating a Custom ShadowJar Task The built in `shadowJar` task only provides an output for the `main` source set of the project. -It is possible to add arbitrary [`ShadowJar`](https://gradleup.com/shadow/api/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.html) +It is possible to add arbitrary [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) tasks to a project. When doing so, ensure that the `configurations` property is specified to inform Shadow which dependencies to merge into the output. From fe402693c7f82d997a2c96fc6fbb07346ab28e10 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 21 Jan 2025 16:10:56 +0800 Subject: [PATCH 164/941] Prepare version 9.0.0-beta5 --- gradle.properties | 2 +- src/docs/changes/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index c67807733..2989f34c8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta5 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 2ecd59b23..05b84067d 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased] + +## [v9.0.0-beta5] (2025-01-21) + **Added** - Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) @@ -475,7 +478,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Tue, 21 Jan 2025 16:11:29 +0800 Subject: [PATCH 165/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 2989f34c8..c67807733 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta5 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 897895c5ab61779843fac60b3ca462ccc1074ba8 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 23 Jan 2025 01:10:32 -0500 Subject: [PATCH 166/941] Fix and polish Log4j2PluginsCacheFileTransformer (#1175) * Copy Log4j2PluginCacheFileTransformer.java from logging-log4j-transform Updated to https://github.com/apache/logging-log4j-transform/blob/a190ba8dfd8a58f1ce95c3d06bcd6a924a416db5/log4j-transform-maven-shade-plugin-extensions/src/main/java/org/apache/logging/log4j/maven/plugins/shade/transformer/Log4j2PluginCacheFileTransformer.java * Convert Log4j2PluginCacheFileTransformer to Kotlin * Merge Log4j2PluginCacheFileTransformer into Log4j2PluginsCacheFileTransformer * Cleanups * Copy Log4j2PluginCacheFileTransformerTest.java Updated to https://github.com/apache/logging-log4j-transform/blob/8d6538acc50ed819d21987e69fe94b485ee0d368/log4j-transform-maven-shade-plugin-extensions/src/test/java/org/apache/logging/log4j/maven/plugins/shade/transformer/Log4j2PluginCacheFileTransformerTest.java * Convert Log4j2PluginCacheFileTransformerTest to Kotlin * Check element.relativePath.pathString in canTransformResource * Fix Log4j2PluginCacheFileTransformerTest * Merge Log4j2PluginCacheFileTransformerTest into Log4j2PluginsCacheFileTransformerTest * Remove redundant shouldTransform and shouldTransformForSingleFile * Cleanups * Revert changes on relocatePlugins * Add a functional test * Note this change --- build.gradle.kts | 1 + src/docs/changes/README.md | 4 + .../gradle/plugins/shadow/BasePluginTest.kt | 2 +- .../shadow/transformers/TransformersTest.kt | 37 +++++++- .../core/config/plugins/Log4j2Plugins.dat | Bin 0 -> 17923 bytes .../Log4j2PluginsCacheFileTransformer.kt | 72 ++++++++------ .../Log4j2PluginsCacheFileTransformerTest.kt | 88 ++++++++++++++++-- .../core/config/plugins/Log4j2Plugins.dat | Bin 0 -> 17923 bytes 8 files changed, 166 insertions(+), 38 deletions(-) create mode 100644 src/functionalTest/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat create mode 100644 src/test/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat diff --git a/build.gradle.kts b/build.gradle.kts index 20f9bdff9..76b3a3099 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -110,6 +110,7 @@ testing.suites { implementation(libs.apache.maven.modelBuilder) implementation(libs.moshi) implementation(libs.moshi.kotlin) + implementation(libs.apache.log4j) } } diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 05b84067d..435fbf8ff 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Fixed** + +- Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) + ## [v9.0.0-beta5] (2025-01-21) 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 088095ef0..6784c94f4 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 @@ -334,7 +334,7 @@ abstract class BasePluginTest { return transform { it.task(taskPath)?.outcome }.isNotNull().isEqualTo(expectedOutcome) } - private fun requireResourceAsPath(name: String): Path { + fun requireResourceAsPath(name: String): Path { val resource = this::class.java.classLoader.getResource(name) ?: throw NoSuchFileException("Resource $name not found.") return resource.toURI().toPath() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index f16e4265d..5cf04f940 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -1,15 +1,20 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import assertk.all import assertk.assertThat import assertk.assertions.isEqualTo +import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull import assertk.assertions.isNull import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.getStream import com.github.jengelman.gradle.plugins.shadow.util.isRegular import java.util.jar.Attributes import kotlin.io.path.appendText +import kotlin.io.path.readText import kotlin.io.path.writeText import kotlin.reflect.KClass +import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource @@ -65,6 +70,37 @@ class TransformersTest : BaseTransformerTest() { assertThat(mf.mainAttributes.getValue("New-Entry")).isNull() } + @Issue( + "https://github.com/GradleUp/shadow/issues/427", + ) + @Test + fun canMergeLog4j2PluginCacheFiles() { + val content = requireResourceAsPath(PLUGIN_CACHE_FILE).readText() + val one = buildJarOne { + insert(PLUGIN_CACHE_FILE, content) + } + val two = buildJarOne { + insert(PLUGIN_CACHE_FILE, content) + } + projectScriptPath.appendText( + transform( + shadowJarBlock = fromJar(one, two), + ), + ) + + run(shadowJarTask) + + val actualFileBytes = outputShadowJar.use { jar -> + @Suppress("Since15") + jar.getStream(PLUGIN_CACHE_FILE).use { it.readAllBytes() } + } + assertThat(actualFileBytes.contentHashCode()).all { + // Hash of the original plugin cache file. + isNotEqualTo(-2114104185) + isEqualTo(1911442937) + } + } + @Test fun canUseCustomTransformer() { writeMainClass() @@ -137,7 +173,6 @@ class TransformersTest : BaseTransformerTest() { "" to ComponentsXmlResourceTransformer::class, "" to DontIncludeResourceTransformer::class, "{ resource.set(\"test.file\"); file.fileValue(file(\"test/some.file\")) }" to IncludeResourceTransformer::class, - "" to Log4j2PluginsCacheFileTransformer::class, "" to ManifestAppenderTransformer::class, "" to ManifestResourceTransformer::class, "{ keyTransformer = { it.toLowerCase() } }" to PropertiesFileTransformer::class, diff --git a/src/functionalTest/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat b/src/functionalTest/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat new file mode 100644 index 0000000000000000000000000000000000000000..fbe6359f109cd7ee81b48c87d619391f3af4b478 GIT binary patch literal 17923 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 YS_b!C_m3ocyexEz() - private val relocators = mutableListOf() + /** + * Log4j config files to share across the transformation stages. + */ + private val tempFiles = mutableListOf() + + /** + * [Relocator] instances to share across the transformation stages. + */ + private val tempRelocators = mutableListOf() private var stats: ShadowStats? = null override fun canTransformResource(element: FileTreeElement): Boolean { - return PluginProcessor.PLUGIN_CACHE_FILE == element.name + return PLUGIN_CACHE_FILE == element.relativePath.pathString } override fun transform(context: TransformerContext) { - val temporaryFile = File.createTempFile("Log4j2Plugins", ".dat") - temporaryFile.deleteOnExit() - temporaryFiles.add(temporaryFile) + val temporaryFile = createTempFile("Log4j2Plugins", ".dat") + tempFiles.add(temporaryFile) val fos = temporaryFile.outputStream() context.inputStream.use { it.copyTo(fos) } - relocators.addAll(context.relocators) + tempRelocators.addAll(context.relocators) if (stats == null) { stats = context.stats } } + /** + * @return `true` if any dat file collected. + */ override fun hasTransformedResource(): Boolean { - // This functionality matches the original plugin, however, I'm not clear what - // the exact logic is. From what I can tell temporaryFiles should be never be empty - // if anything has been performed. - return temporaryFiles.isNotEmpty() || relocators.isNotEmpty() + return tempFiles.isNotEmpty() } override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val pluginCache = PluginCache() - pluginCache.loadCacheFiles(urlEnumeration) - relocatePlugins(pluginCache) - val entry = ZipEntry(PluginProcessor.PLUGIN_CACHE_FILE) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) - pluginCache.writeCache(CloseShieldOutputStream.wrap(os)) - temporaryFiles.clear() + try { + val aggregator = PluginCache() + aggregator.loadCacheFiles(urlEnumeration) + relocatePlugins(aggregator) + val entry = ZipEntry(PLUGIN_CACHE_FILE) + entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) + os.putNextEntry(entry) + // prevent the aggregator to close the jar output. + aggregator.writeCache(CloseShieldOutputStream.wrap(os)) + } finally { + deleteTempFiles() + } } - private fun relocatePlugins(pluginCache: PluginCache) { + internal fun relocatePlugins(pluginCache: PluginCache) { pluginCache.allCategories.values.forEach { currentMap -> currentMap.values.forEach { currentPluginEntry -> val className = currentPluginEntry.className val relocateClassContext = RelocateClassContext(className, requireNotNull(stats)) - relocators.firstOrNull { it.canRelocateClass(className) }?.let { relocator -> + tempRelocators.firstOrNull { it.canRelocateClass(className) }?.let { relocator -> // Then we perform that relocation and update the plugin entry to reflect the new value. currentPluginEntry.className = relocator.relocateClass(relocateClassContext) } @@ -80,9 +91,18 @@ public open class Log4j2PluginsCacheFileTransformer : Transformer { } } + private fun deleteTempFiles() { + val pathIterator = tempFiles.listIterator() + while (pathIterator.hasNext()) { + val path = pathIterator.next() + path.deleteIfExists() + pathIterator.remove() + } + } + private val urlEnumeration: Enumeration get() { - val urls = temporaryFiles.map { it.toURI().toURL() } + val urls = tempFiles.map { it.toUri().toURL() } return Collections.enumeration(urls) } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index 3a24dec71..19c082d4c 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -1,29 +1,41 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import assertk.all import assertk.assertThat import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isNotEqualTo import assertk.assertions.isTrue +import assertk.fail import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import com.github.jengelman.gradle.plugins.shadow.util.SimpleRelocator +import java.io.ByteArrayOutputStream import java.io.File import java.net.URI +import java.net.URL import java.util.Collections +import java.util.jar.JarInputStream import org.apache.logging.log4j.core.config.plugins.processor.PluginCache +import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE import org.apache.tools.zip.ZipOutputStream import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource +/** + * Modified from [org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformerTest.java](https://github.com/apache/logging-log4j-transform/blob/main/log4j-transform-maven-shade-plugin-extensions/src/test/java/org/apache/logging/log4j/maven/plugins/shade/transformer/Log4j2PluginCacheFileTransformerTest.java). + */ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest() { @Test - fun shouldTransform() { - transformer.transform(context(SimpleRelocator())) - assertThat(transformer.hasTransformedResource()).isTrue() - } - - @Test - fun shouldTransformForSingleFile() { - transformer.transform(context()) - assertThat(transformer.hasTransformedResource()).isTrue() + fun canTransformResource() { + assertThat(transformer.canTransformResource("")).isFalse() + assertThat(transformer.canTransformResource(".")).isFalse() + assertThat(transformer.canTransformResource("tmp.dat")).isFalse() + assertThat(transformer.canTransformResource("$PLUGIN_CACHE_FILE.tmp")).isFalse() + assertThat(transformer.canTransformResource("tmp/$PLUGIN_CACHE_FILE")).isFalse() + assertThat(transformer.canTransformResource(PLUGIN_CACHE_FILE)).isTrue() } @Test @@ -48,11 +60,67 @@ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest + while (true) { + val jarEntry = inputStream.nextJarEntry + if (jarEntry == null) { + fail("No expected resource in the output jar.") + } else if (jarEntry.name == PLUGIN_CACHE_FILE) { + @Suppress("Since15") + assertThat(inputStream.readAllBytes().contentHashCode()).all { + // Hash of the original plugin cache file. + isNotEqualTo(-2114104185) + isEqualTo(1911442937) + } + break + } + } + } + } + + @ParameterizedTest + @MethodSource("relocationParameters") + fun relocations(pattern: String, shadedPattern: String, target: String) { + val aggregator = PluginCache().apply { + val resources = Collections.enumeration(listOf(pluginCacheUrl)) + loadCacheFiles(resources) + } + transformer.transform(context(SimpleRelocator(pattern, shadedPattern))) + transformer.relocatePlugins(aggregator) + + for (pluginEntryMap in aggregator.allCategories.values) { + for (entry in pluginEntryMap.values) { + assertThat(entry.className.startsWith(target)).isTrue() + } + } + } + private fun context(vararg relocator: SimpleRelocator): TransformerContext { return TransformerContext(PLUGIN_CACHE_FILE, requireResourceAsStream(PLUGIN_CACHE_FILE), relocator.toSet()) } private companion object { - const val PLUGIN_CACHE_FILE = "META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat" + val pluginCacheUrl: URL = requireNotNull(this::class.java.classLoader.getResource(PLUGIN_CACHE_FILE)) + + @JvmStatic + fun relocationParameters() = listOf( + // test with matching relocator + Arguments.of("org.apache.logging", "new.location.org.apache.logging", "new.location.org.apache.logging"), + // test without matching relocator + Arguments.of("com.apache.logging", "new.location.com.apache.logging", "org.apache.logging"), + ) } } diff --git a/src/test/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat b/src/test/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat new file mode 100644 index 0000000000000000000000000000000000000000..fbe6359f109cd7ee81b48c87d619391f3af4b478 GIT binary patch literal 17923 zcmcIsS#un>5r!jjd5D%|TOuWwtSVReC6)3dapc?=FWIC?n%<@CxKeqToxv`LoJ(_1 z-2D1{jbmn)m|Z~j=E0U0Kz|K1x*OdMj2L4*){C?3y3QG6@3UUw7uURCzs-uV=jVQK z$-OumkE3)qVP_|;1fx%Ng3O`x=D&U^^0-I_ML!^liPlsR#C!_(!8#A zi*cRsv?>+!G$3X?tkW>&>`zY6eO_jD5%4`hr01$27+bmWZ+!M^C#Yi#RREF<psXF5v(}^#lYNW4|avM^O%{q7JIM z;2~hNB#4}`d*!5Le{ovFU{Y2*IWMxD7gfad9t#8j1=TJ4(g|okODld`wIUU?fZ*v3 z`@IvyVR{o4SxUO5q#}rck@z|DoG_;N6A`IY8b*%$ag3pQKLi=b$-)40%^sD!i2OME zFArmXlGT+}Fk+N9Xvj_^Sx=I`$Jo|2pWOIy&2v90Y;&xuDE7YLlkbV(oCvn2e*x}M z%x`#H`01FBqPT+NuxyR5c(LuAwlq0;B1#!Q4J?a7Dl5e z@T&;k3X00k7f(ye#VfKnrc)Ps0#Xm!csT&#-sE%&kyM{;cei5>?-!%s%a>ogR9kO5 zrlb}0C5^G`A3&LKR>5-9N&(uOtOSHE;oLeTIsQoIQ`2i ziQh>Lz6ODz2EDgMy zR~vnQsK9gs`7PC?G{VhqDO^J-?h6}&=Nj9?6M0?>GlI(k0c4ERl}QZQg!yfVaYdF@ z?4OHQJ_qo#K4>MJ0;2{Xf9My5Kbeg&Z2m;V7`sH*8B5HIFdkNy1urkNI8@P=#mneu zOH&yk*QN?t5YyYjKv=+3&;kPG6s8~`j3*JCc~B>HjI4!b$xFn->>G>7b>MO51U(w< zwqTe%9}w7+&So%QDU&K^zjNd@z~D}U*|=>Sn=PG?r{%vo{Mt?{*@6onmuGPlOxP>O z=_x85B|_kRiE}P--d6+UPptmF5*`ViS*+XF0`JR^g=E_)i_K_6cq!shtKDc!V-pGe zj1V@DM*8oV@FucZUuOF3$Wbc#=%;K}p6W1n`?>!p;R!-_Bzw8kN^2IN7jr5IkuZXA zu9WUw<-RQ;Z$wT@$bIiP_q!!N@f-DX3$K0zf#E_|>2TxDVaW7R$k{5Li2S|x*!`d` zt1J;Iqb+Pu4q{(Wh-^`N;;AN~r+mzBp;dJ3TWTnBl-DN;NS`R$wDVb(mX%*rbzT(_ zd48Pz%E-QUR41*f-=+hJbkQIwGkZ3-3rl$%pb}v)x75!XMbOOm0b=ld+|>$}y%M=B zx0V$eCId0NThqoo9;x%%P1C*tfuU(@GD0eCB{iiVGVal*M?(x@BJfeQDsl`;K`a$R zr699qE>lzJ5E}X=4~@-wS52W4XpfML9eXyn3)}XL63OMvfD*_#Gnm_j8EoN9u8`H1 zRhX9SkBbc?<&}4;#~-BS{LEbiX_n%%+WVOR4K-{sT1;(4gk^tSJf#kZ>5xod0P7Jd zTVa+U2;pT3?XcH-w5LaH3xo(bKnM-$J`a0A7S~DY9G&a)YG05khuD+9Fx&guPN{Zb z@r6nN+LWn!aewLuZLvoC{HMXq&=X#eQH4Jpr&*ReFL_@Pon_y4$5&f2A(5tQtMkcG z>e>R^GJ6E9pqo2Kd?4@M)m22TaqPT8?YLE)2jM7)SH&NY+8Q>h%rGrWsYP#=2d$v- zdM{^-0OCNS@cdmJ)qa-bS&7`*a8&E!Xagoqw%~c}2iD1|>`tDR#S-AHhe=CdanB8GTmkZ>~p zS|8yc;*>Z$b2=c*k8m4sO);mRmLpzBYqAw5MQP|n5MC%kr`xCPaDps73htJOEN(@a z$AKg!CoJkqhNY}t`=e{$xwJ~wD=55gh)%2W%-QxZ!jpi!F2-DCZ8p&~yN~RY?pcmN ze@^r&S9^G;0b9_!;zgFcajsp^yd{Zu49e3toio1&gR(g?ZFhq*o;A38Xu-_3WZER< z?-&Ki+{xIREg4CVA|8jO4N4|_o(#obNs_JUUzi)B0#UMZ-fnG=${0I^wvKyj>9H&- zsU?St>kfaluA6ChL)^fi%CYy;-1dQ>-W7LHtS5f~btSD4&$*Tn&oH=iy_?MjTVHy} zk?LX-+lxwBv(=n*WvUEg-C%@CRH&UQx|D2d`WFB)t34G?Bg%geJ%2pF_^bv8jA3pMA`fqMfVql1ljus^rSY zAdBnKTT&}jfyq)JP+m!DhEZbbiDIVpilR}lnQCUqdZL_3>CO{rX)2p}$Gb3TlIqS% zUwyHTyRbDWb=Qj6iP$4;Sq(9)6vjs{CQ$Wc?3e7R%&($QO~3869Gp6NlPT6wsFtwb zpg*14gCGVLX)v@@iR8oJZ8^v_Z#X(UiIf-y`{xULVO5ELiyN(R zb!n@DYIdjo?TH|0_lrPe8%*Los@)l+|voM#9?g3=eOM=IX2C+T;tRxf=Vz(vPaPTw8=nY{5~(=j?cK_%XZ6 zBSE(hy3X|~^mY_<`f*)%95j||WzF?9XpJ3w+}R3yyo|<|G5)Ju?eS|(&~Dij2+V@+ z6XrWON3)>k9rHceCyTr3-2CZ|{ItuFcQvA5li<3-M+$w{ja$k>daF()xv^V2^E%+d z3y#+v(G90NErYM43-2ooy0S8uKVR63Fir%s25U=Rsm9Bf%m&XVBBnK(8GD-gD9?5+ z)iLZk1AXT*pU&-J>{FD`=`n6NUyr|XbO8!zzAk@yXdQyr%@6`l@@isx%%jEpy^ojh z2rx<%rVoj*)Atap=p~cShwe5+K-k{wf6+;6Qg%OHU@X@%_&-Mh4f|);q4`^{+1wz9|VqqLy$G$sDHm@j=pDsiv1{oG$3Lvcsg7k=0%j@jqgOQ zH)xVn$5;z)4k+ge8xJAlyT?kww3(v?hANIxf?tkD*#fWhNQRWwByAx zWumQm;j22f6XAv#^)h1)j=|KCBsV6pGp_^D1~8ZITecms;Pk5!=vO*@SS-bun2hF8H?Cq zo5N@v@*qn5nEl`;2Jx*(iI0v&?b5pdCMF{5nB`}C(fB~(HH~buZA27EER`KDjis`` zS(d61EZ2tk*pDNOxLC>(C0NUBZBM}?*&R+QE@HC)STqiF-b2Mxa^N|6}ck>Z1fspudjAZhAkD1#J zuymnOcqbjn%Bg z8NLTxiu*eOQp;h5ev-IUsJI-bU<8h9ss)_zFAD^_5v;>48E;NFq?% zB|f^_g-NX9aZs@&4IB_u%WhTt29F-961Ztwj%&a(Enu}=da?m*?m?KgFS`RL_w%}l YS_b!C_m3ocyexEz Date: Thu, 23 Jan 2025 04:43:24 -0500 Subject: [PATCH 167/941] Exclude module-info.class in Multi-Release folders by default (#1177) --- src/docs/changes/README.md | 4 ++++ .../github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 4 ++++ .../jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt | 2 ++ 3 files changed, 10 insertions(+) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 435fbf8ff..350aa7ec2 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Added** + +- Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) + **Fixed** - Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 943754bc6..bd012d4b4 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -207,6 +207,8 @@ class JavaPluginTest : BasePluginTest() { insert("META-INF/a.DSA", "DSA Signature Block") insert("META-INF/a.RSA", "RSA Signature Block") insert("META-INF/a.properties", "key=value") + insert("META-INF/versions/9/module-info.class", "module myModuleName {}") + insert("META-INF/versions/16/module-info.class", "module myModuleName {}") insert("module-info.class", "module myModuleName {}") } }.publish() @@ -238,6 +240,8 @@ class JavaPluginTest : BasePluginTest() { "META-INF/a.SF", "META-INF/a.DSA", "META-INF/a.RSA", + "META-INF/versions/9/module-info.class", + "META-INF/versions/16/module-info.class", "module-info.class", ) } 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 ab83f8e66..edcf9d3f3 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 @@ -96,6 +96,8 @@ public abstract class ShadowJavaPlugin @Inject constructor( "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", ) } From 3f593639169ee990bea10e3b4802313a7f10af92 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 23 Jan 2025 06:03:03 -0500 Subject: [PATCH 168/941] Tidy up @Issue (#1179) --- .../github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 4 ++++ .../gradle/plugins/shadow/transformers/TransformersTest.kt | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index bd012d4b4..001ff5821 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -197,6 +197,10 @@ class JavaPluginTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/352", + "https://github.com/GradleUp/shadow/issues/729", + ) @Test fun excludeSomeMetaInfFilesByDefault() { localRepo.module("shadow", "a", "1.0") { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 5cf04f940..48bf68dbd 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -53,7 +53,9 @@ class TransformersTest : BaseTransformerTest() { commonAssertions() } - @Issue("https://github.com/GradleUp/shadow/issues/82") + @Issue( + "https://github.com/GradleUp/shadow/issues/82", + ) @Test fun shadowManifestLeaksToJarManifest() { writeMainClass() From 1f543ed6791c87a2331233437e95e8875f1ecd8c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 23 Jan 2025 06:23:56 -0500 Subject: [PATCH 169/941] Note using Log4j2PluginsCacheFileTransformer in doc (#1178) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Piotr P. Karwasz --- src/docs/configuration/merging/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/docs/configuration/merging/README.md b/src/docs/configuration/merging/README.md index 5c224fa91..b801f0785 100644 --- a/src/docs/configuration/merging/README.md +++ b/src/docs/configuration/merging/README.md @@ -176,6 +176,18 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow } ``` +## Merging Log4j2 Plugin Cache Files (`Log4j2Plugins.dat`) + +`Log4j2PluginsCacheFileTransformer` is a `Transformer` that merges `META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat` plugin caches from all the jars +containing Log4j 2.x Core components. It's a Gradle equivalent of [Log4j Plugin Descriptor Transformer](https://logging.apache.org/log4j/transform/log4j-transform-maven-shade-plugin-extensions.html#log4j-plugin-cache-transformer). + +```groovy +// Merging Log4j2 Plugin Cache Files +tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer.class) +} +``` + ## Appending Text Files Generic text files can be appended together using the From fe2931feacc76e2ebd11da670cbeb931b06bc0ad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Jan 2025 11:31:08 +0000 Subject: [PATCH 170/941] Update plugin com.gradle.develocity to v3.19.1 (#1180) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 4bb3ca1a1..0066caee7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "3.19" + id("com.gradle.develocity") version "3.19.1" } develocity { From a7bffadffdfccecc95bb374831d35c29b0e3a8e8 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 23 Jan 2025 19:39:51 +0800 Subject: [PATCH 171/941] Prepare version 9.0.0-beta6 --- gradle.properties | 2 +- src/docs/changes/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index c67807733..93e6e7b35 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta6 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 350aa7ec2..a27858a31 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased] + +## [v9.0.0-beta6] (2025-01-23) + **Added** - Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) @@ -486,7 +489,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Thu, 23 Jan 2025 19:40:44 +0800 Subject: [PATCH 172/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 93e6e7b35..c67807733 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta6 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 5247c1ed5cb7e5682c27f6d89bb42da813ca3f76 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 23 Jan 2025 19:42:11 +0800 Subject: [PATCH 173/941] Update RELEASING.md --- RELEASING.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 8cd3e99a3..0c3a226e9 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -12,28 +12,28 @@ 4. Commit - ``` - $ git commit -am "Prepare version X.Y.Z" + ```sh + git commit -am "Prepare version X.Y.Z" ``` 5. Tag - ``` - $ git tag -am "Version X.Y.Z" X.Y.Z + ```sh + git tag -am "Version X.Y.Z" X.Y.Z ``` 6. Update the `VERSION_NAME` in `gradle.properties` to the next "SNAPSHOT" version. 7. Commit - ``` - $ git commit -am "Prepare next development version" + ```sh + git commit -am "Prepare next development version" ``` 8. Push! - ``` - $ git push && git push --tags + ```sh + git push && git push --tags ``` This will trigger a GitHub Action workflow which will create a GitHub release and upload the From ee5593630f39e6c7765f19fba200b99fed240b17 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 23 Jan 2025 09:33:11 -0500 Subject: [PATCH 174/941] Say "Hello, World!" (#1181) * Reuse writeMainClass in ApplicationPluginTest * Tweak writeGradlePluginModule --- .../plugins/shadow/ApplicationPluginTest.kt | 21 ++++++------------- .../gradle/plugins/shadow/BasePluginTest.kt | 5 +++-- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index d5f7fdba7..d2e8d437b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -45,7 +45,7 @@ class ApplicationPluginTest : BasePluginTest() { assertThat(result.output).contains( "Running application with JDK 17", - "TestApp: Hello World! (foo)", + "Hello, World! (foo)", ) commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) @@ -86,7 +86,7 @@ class ApplicationPluginTest : BasePluginTest() { val extractedJar = path("extracted/myapp-1.0-all.jar") zip.getStream("myapp-shadow-1.0/lib/myapp-1.0-all.jar") .use { it.copyTo(extractedJar.outputStream()) } - commonAssertions(JarPath(extractedJar), entriesContained = arrayOf("myapp/Main.class")) + commonAssertions(JarPath(extractedJar), entriesContained = arrayOf("shadow/Main.class")) assertThat(zip.getContent("myapp-shadow-1.0/bin/myapp")).contains( "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", @@ -114,22 +114,13 @@ class ApplicationPluginTest : BasePluginTest() { dependenciesBlock: String = "implementation 'shadow:a:1.0'", runShadowBlock: String = "", ) { - path("src/main/java/myapp/Main.java").appendText( - """ - package myapp; - public class Main { - public static void main(String[] args) { - System.out.println("TestApp: Hello World! (" + args[0] + ")"); - } - } - """.trimIndent(), - ) + writeMainClass() projectScriptPath.appendText( """ apply plugin: 'application' $projectBlock application { - mainClass = 'myapp.Main' + mainClass = 'shadow.Main' } dependencies { $dependenciesBlock @@ -150,11 +141,11 @@ class ApplicationPluginTest : BasePluginTest() { private fun commonAssertions( jarPath: JarPath, - entriesContained: Array = arrayOf("a.properties", "a2.properties", "myapp/Main.class"), + entriesContained: Array = arrayOf("a.properties", "a2.properties", "shadow/Main.class"), ) { assertThat(jarPath).useAll { containsEntries(*entriesContained) - getMainAttr("Main-Class").isEqualTo("myapp.Main") + getMainAttr("Main-Class").isEqualTo("shadow.Main") } } 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 6784c94f4..54d85eaf2 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 @@ -164,7 +164,8 @@ abstract class BasePluginTest { $imports public class Main { public static void main(String[] args) { - System.out.println("Hello, World!"); + String content = String.format("Hello, World! (%s)", args); + System.out.println(content); } } """.trimIndent(), @@ -252,7 +253,7 @@ abstract class BasePluginTest { import org.gradle.api.Project; public class MyPlugin implements Plugin { public void apply(Project project) { - System.out.println("MyPlugin: Hello World!"); + System.out.println("MyPlugin: Hello, World!"); } } """.trimIndent(), From cb08953e59a51553a02efb1930557fbf74e45a33 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 23 Jan 2025 10:07:18 -0500 Subject: [PATCH 175/941] Support overriding mainClass provided by JavaApplication (#1182) * Add canOverrideMainClassAttrInManifestBlock * Check attr key before injecting * Note this change --- src/docs/changes/README.md | 4 +++ .../plugins/shadow/ApplicationPluginTest.kt | 33 +++++++++++++++++-- .../gradle/plugins/shadow/BasePluginTest.kt | 7 ++-- .../plugins/shadow/ShadowApplicationPlugin.kt | 5 ++- 4 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index a27858a31..616e9e252 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Fixed** + +- Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) + ## [v9.0.0-beta6] (2025-01-23) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index d2e8d437b..009d74479 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -8,6 +8,7 @@ import assertk.assertions.exists import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.getContent @@ -45,7 +46,7 @@ class ApplicationPluginTest : BasePluginTest() { assertThat(result.output).contains( "Running application with JDK 17", - "Hello, World! (foo)", + "Hello, World! (foo) from Main", ) commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) @@ -108,6 +109,33 @@ class ApplicationPluginTest : BasePluginTest() { commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) } + @Issue( + "https://github.com/GradleUp/shadow/issues/613", + ) + @Test + fun canOverrideMainClassAttrInManifestBlock() { + writeMainClass(className = "Main2") + prepare( + projectBlock = """ + shadowJar { + manifest { + attributes 'Main-Class': 'shadow.Main2' + } + } + """.trimIndent(), + ) + + val result = run(SHADOW_RUN_TASK_NAME) + + assertThat(result.output).contains( + "Hello, World! (foo) from Main2", + ) + commonAssertions( + jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"), + mainClassAttr = "shadow.Main2", + ) + } + private fun prepare( projectBlock: String = "", settingsBlock: String = "", @@ -142,10 +170,11 @@ class ApplicationPluginTest : BasePluginTest() { private fun commonAssertions( jarPath: JarPath, entriesContained: Array = arrayOf("a.properties", "a2.properties", "shadow/Main.class"), + mainClassAttr: String = "shadow.Main", ) { assertThat(jarPath).useAll { containsEntries(*entriesContained) - getMainAttr("Main-Class").isEqualTo("shadow.Main") + getMainAttr("Main-Class").isEqualTo(mainClassAttr) } } 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 54d85eaf2..7a9bc607c 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 @@ -155,16 +155,17 @@ abstract class BasePluginTest { fun writeMainClass( withImports: Boolean = false, + className: String = "Main", ) { val imports = if (withImports) "import junit.framework.Test;" else "" - path("src/main/java/shadow/Main.java").writeText( + path("src/main/java/shadow/$className.java").writeText( """ package shadow; $imports - public class Main { + public class $className { public static void main(String[] args) { - String content = String.format("Hello, World! (%s)", args); + String content = String.format("Hello, World! (%s) from $className", args); System.out.println(content); } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 0e0fec3c7..998abe74d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -36,7 +36,10 @@ public abstract class ShadowApplicationPlugin : Plugin { shadowJar.configure { jar -> jar.inputs.property("mainClassName", classNameProvider) jar.doFirst { - jar.manifest.attributes["Main-Class"] = classNameProvider.get() + // Inject the Main-Class attribute if it is not already present. + if (!jar.manifest.attributes.contains("Main-Class")) { + jar.manifest.attributes["Main-Class"] = classNameProvider.get() + } } } } From caf8d8913d9d51f6f499b556e3e0774179f82839 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 24 Jan 2025 00:25:15 -0500 Subject: [PATCH 176/941] Test start scripts running (#1184) * Simplify integrationWithApplicationPluginAndJavaToolchains and shadowApplicationDistributionsShouldUseShadowJar * Better jar entry checks in shadowApplicationDistributionsShouldUseShadowJar * Normalize separator for Windows * Add runProcess * Depend on junit-3.8.2.jar * Add walkEntries * Rename * Run scripts in runProcess * Check Class-Path in commonAssertions * Cleanups * Remove preCommands for OS --- .../plugins/shadow/ApplicationPluginTest.kt | 150 +++++++++++++----- .../gradle/plugins/shadow/BasePluginTest.kt | 11 +- .../gradle/plugins/shadow/util/RunProcess.kt | 19 +++ 3 files changed, 136 insertions(+), 44 deletions(-) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 009d74479..d4bf0edaf 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -1,30 +1,38 @@ package com.github.jengelman.gradle.plugins.shadow -import assertk.all import assertk.assertThat import assertk.assertions.contains import assertk.assertions.containsOnly -import assertk.assertions.exists import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.getContent import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr -import com.github.jengelman.gradle.plugins.shadow.util.getStream +import com.github.jengelman.gradle.plugins.shadow.util.isWindows +import com.github.jengelman.gradle.plugins.shadow.util.runProcess +import java.nio.file.FileSystems +import java.nio.file.Path import java.util.zip.ZipFile +import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.appendText +import kotlin.io.path.createDirectories +import kotlin.io.path.isRegularFile import kotlin.io.path.outputStream import kotlin.io.path.readText +import kotlin.io.path.relativeTo +import kotlin.io.path.walk import kotlin.io.path.writeText import org.junit.jupiter.api.Test +@ExperimentalPathApi class ApplicationPluginTest : BasePluginTest() { @Test fun integrationWithApplicationPluginAndJavaToolchains() { prepare( + mainClassWithImports = true, + dependenciesBlock = "implementation 'junit:junit:3.8.2'", projectBlock = """ java { toolchain.languageVersion = JavaLanguageVersion.of(17) @@ -47,57 +55,105 @@ class ApplicationPluginTest : BasePluginTest() { assertThat(result.output).contains( "Running application with JDK 17", "Hello, World! (foo) from Main", + "Refs: junit.framework.Test", ) - commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) + val installPath = path("build/install/") + assertThat(installPath.walkEntries()).containsOnly( + "myapp-shadow/bin/myapp", + "myapp-shadow/bin/myapp.bat", + "myapp-shadow/lib/myapp-1.0-all.jar", + ) - assertThat(path("build/install/myapp-shadow/bin/myapp")).all { - exists() - transform { it.readText() }.contains( - "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", - "-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"", - "exec \"\$JAVACMD\" \"\$@\"", - ) - } - assertThat(path("build/install/myapp-shadow/bin/myapp.bat")).all { - exists() - transform { it.readText() }.contains( - "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", - ) + commonAssertions( + jarPath("myapp-shadow/lib/myapp-1.0-all.jar", installPath), + entriesContained = arrayOf("shadow/Main.class", "junit/framework/Test.class"), + ) + + val unixScript = path("myapp-shadow/bin/myapp", installPath) + val winScript = path("myapp-shadow/bin/myapp.bat", installPath) + + assertThat(unixScript.readText()).contains( + "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", + "-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"", + "exec \"\$JAVACMD\" \"\$@\"", + ) + assertThat(winScript.readText()).contains( + "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", + ) + + val runningOutput = if (isWindows) { + runProcess(winScript.toString(), "bar") + } else { + runProcess(unixScript.toString(), "bar") } + assertThat(runningOutput).contains( + "Hello, World! (bar) from Main", + "Refs: junit.framework.Test", + ) } @Test fun shadowApplicationDistributionsShouldUseShadowJar() { prepare( - dependenciesBlock = "shadow 'shadow:a:1.0'", + mainClassWithImports = true, + dependenciesBlock = "shadow 'junit:junit:3.8.2'", ) run("shadowDistZip") - ZipFile(path("build/distributions/myapp-shadow-1.0.zip").toFile()).use { zip -> - val fileEntries = zip.entries().toList().map { it.name }.filter { !it.endsWith("/") } - assertThat(fileEntries).containsOnly( - "myapp-shadow-1.0/bin/myapp", - "myapp-shadow-1.0/bin/myapp.bat", - "myapp-shadow-1.0/lib/myapp-1.0-all.jar", - "myapp-shadow-1.0/lib/a-1.0.jar", - ) - - val extractedJar = path("extracted/myapp-1.0-all.jar") - zip.getStream("myapp-shadow-1.0/lib/myapp-1.0-all.jar") - .use { it.copyTo(extractedJar.outputStream()) } - commonAssertions(JarPath(extractedJar), entriesContained = arrayOf("shadow/Main.class")) - - assertThat(zip.getContent("myapp-shadow-1.0/bin/myapp")).contains( - "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", - "-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"", - "exec \"\$JAVACMD\" \"\$@\"", - ) - assertThat(zip.getContent("myapp-shadow-1.0/bin/myapp.bat")).contains( - "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", - ) + val zipPath = path("build/distributions/myapp-shadow-1.0.zip") + val extractedPath = path("build/distributions/extracted/") + + // Unzip the whole file. + ZipFile(zipPath.toFile()).use { zip -> + zip.entries().toList().forEach { entry -> + val entryDest = extractedPath.resolve(entry.name) + if (entry.isDirectory) { + entryDest.createDirectories() + } else { + zip.getInputStream(entry).use { it.copyTo(entryDest.outputStream()) } + } + } + } + + assertThat(extractedPath.walkEntries()).containsOnly( + "myapp-shadow-1.0/bin/myapp", + "myapp-shadow-1.0/bin/myapp.bat", + "myapp-shadow-1.0/lib/myapp-1.0-all.jar", + "myapp-shadow-1.0/lib/junit-3.8.2.jar", + ) + + commonAssertions( + jarPath("myapp-shadow-1.0/lib/myapp-1.0-all.jar", extractedPath), + entriesContained = arrayOf("shadow/Main.class"), + classPathAttr = "junit-3.8.2.jar", + ) + + val unixScript = path("myapp-shadow-1.0/bin/myapp", extractedPath) + val winScript = path("myapp-shadow-1.0/bin/myapp.bat", extractedPath) + + assertThat(unixScript.readText()).contains( + "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", + "-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"", + "exec \"\$JAVACMD\" \"\$@\"", + ) + assertThat(winScript.readText()).contains( + "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", + ) + + val runningOutput = if (isWindows) { + // Use `cmd` and `/c` explicitly to prevent `The process cannot access the file because it is being used by another process` errors. + runProcess("cmd", "/c", winScript.toString(), "bar") + } else { + // Use `sh` explicitly since the extracted script is non-executable. + // Avoid `chmod +x` to prevent `Text file busy` errors on Linux. + runProcess("sh", unixScript.toString(), "bar") } + assertThat(runningOutput).contains( + "Hello, World! (bar) from Main", + "Refs: junit.framework.Test", + ) } @Test @@ -137,12 +193,13 @@ class ApplicationPluginTest : BasePluginTest() { } private fun prepare( + mainClassWithImports: Boolean = false, projectBlock: String = "", settingsBlock: String = "", dependenciesBlock: String = "implementation 'shadow:a:1.0'", runShadowBlock: String = "", ) { - writeMainClass() + writeMainClass(withImports = mainClassWithImports) projectScriptPath.appendText( """ apply plugin: 'application' @@ -171,14 +228,23 @@ class ApplicationPluginTest : BasePluginTest() { jarPath: JarPath, entriesContained: Array = arrayOf("a.properties", "a2.properties", "shadow/Main.class"), mainClassAttr: String = "shadow.Main", + classPathAttr: String? = null, ) { assertThat(jarPath).useAll { containsEntries(*entriesContained) getMainAttr("Main-Class").isEqualTo(mainClassAttr) + getMainAttr("Class-Path").isEqualTo(classPathAttr) } } private companion object { val runShadow = "tasks.named('$SHADOW_RUN_TASK_NAME')".trim() + + fun Path.walkEntries(): Sequence { + return walk() + .filter { it.isRegularFile() } + .map { it.relativeTo(this) } + .map { it.toString().replace(FileSystems.getDefault().separator, "/") } + } } } 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 7a9bc607c..a7c139d15 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 @@ -20,6 +20,7 @@ import kotlin.io.path.Path import kotlin.io.path.absolutePathString import kotlin.io.path.appendText import kotlin.io.path.createDirectories +import kotlin.io.path.createDirectory import kotlin.io.path.createFile import kotlin.io.path.createTempDirectory import kotlin.io.path.deleteRecursively @@ -134,8 +135,12 @@ abstract class BasePluginTest { return parent.resolve(relative).also { if (it.exists()) return@also it.parent.createDirectories() - // We should create text file only if it doesn't exist. - it.createFile() + if (relative.endsWith("/")) { + it.createDirectory() + } else { + // We should create text file only if it doesn't exist. + it.createFile() + } } } @@ -158,6 +163,7 @@ abstract class BasePluginTest { className: String = "Main", ) { val imports = if (withImports) "import junit.framework.Test;" else "" + val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" path("src/main/java/shadow/$className.java").writeText( """ @@ -167,6 +173,7 @@ abstract class BasePluginTest { public static void main(String[] args) { String content = String.format("Hello, World! (%s) from $className", args); System.out.println(content); + System.out.println($classRef); } } """.trimIndent(), diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt new file mode 100644 index 000000000..c15413598 --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt @@ -0,0 +1,19 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +fun runProcess( + vararg commands: String, +): String { + val process = ProcessBuilder(commands.toList()).start() + val exitCode = process.waitFor() + + val err = process.errorStream.bufferedReader().use { it.readText() } + val out = process.inputStream.bufferedReader().use { it.readText() } + + if (exitCode != 0 || err.isNotEmpty()) { + error("Error occurred when running command line: $err") + } + + return out +} + +val isWindows = System.getProperty("os.name").startsWith("Windows") From 220809f179dd4eb143c25db3db136bd51548a18b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 24 Jan 2025 01:34:17 -0500 Subject: [PATCH 177/941] Update start script templates (#1183) * Update unixStartScript.txt and windowsStartScript.txt from https://github.com/gradle/gradle/tree/e226beaa17cf21689966bfd4e9cad846ad6ded2d/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins * Fix integrationWithApplicationPluginAndJavaToolchains and shadowApplicationDistributionsShouldUseShadowJar * Add downloadStartScripts task * Run downloadStartScripts for testing * Check the dest dir * Update changelog * Update description --- build.gradle.kts | 17 + src/docs/changes/README.md | 4 + .../plugins/shadow/ApplicationPluginTest.kt | 2 - .../shadow/internal/unixStartScript.txt | 327 ++++++++++++------ .../shadow/internal/windowsStartScript.txt | 78 +++-- 5 files changed, 283 insertions(+), 145 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 76b3a3099..362ced927 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -145,6 +145,23 @@ tasks.check { dependsOn(tasks.withType()) } +tasks.register("downloadStartScripts") { + description = "Download start scripts from Gradle sources, this should be run intervally to track updates." + + val urlPrefix = "https://raw.githubusercontent.com/gradle/gradle/refs/heads/master/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins" + from(resources.text.fromUri("$urlPrefix/unixStartScript.txt")) { + rename { "unixStartScript.txt" } + } + from(resources.text.fromUri("$urlPrefix/windowsStartScript.txt")) { + rename { "windowsStartScript.txt" } + } + val destDir = file("src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal") + if (!destDir.exists() || !destDir.isDirectory || destDir.listFiles().isNullOrEmpty()) { + error("Download destination dir $destDir does not exist or is empty.") + } + into(destDir) +} + tasks.clean { val includedBuilds = gradle.includedBuilds dependsOn(includedBuilds.map { it.task(path) }) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 616e9e252..06abc4dd0 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Changed** + +- Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) + **Fixed** - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index d4bf0edaf..442da2e25 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -75,7 +75,6 @@ class ApplicationPluginTest : BasePluginTest() { assertThat(unixScript.readText()).contains( "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", - "-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"", "exec \"\$JAVACMD\" \"\$@\"", ) assertThat(winScript.readText()).contains( @@ -135,7 +134,6 @@ class ApplicationPluginTest : BasePluginTest() { assertThat(unixScript.readText()).contains( "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", - "-jar \"\\\"\$CLASSPATH\\\"\" \"\$APP_ARGS\"", "exec \"\$JAVACMD\" \"\$@\"", ) assertThat(winScript.readText()).contains( diff --git a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt index 83526b7f4..f082fb120 100644 --- a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt +++ b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,80 +15,150 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## -## -## ${applicationName} start up script for UN*X -## +# +# ${applicationName} start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh ${applicationName} +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «\$var», «\${var}», «\${var:-default}», «\${var+SET}», +# «\${var#prefix}», «\${var%suffix}», and «\$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "\$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and ${optsEnvironmentVar}) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +#<% /* +# ... and if you're reading this, this IS the template just mentioned. +# +# This template is processed by +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/java/org/gradle/api/internal/plugins/UnixStartScriptGenerator.java +# +# Gradle is a meta-build system used by the project that you're building +# or installing. It's like autoconf but for projects that are written in +# Java and related languages. It's also used to build parts of the Gradle +# project itself. +# +# The Groovy template language is run in two phases. +# +# 1. Any character following \ is passed unmodified through to the +# next phase, while the \ is removed. Any other $ followed by +# varName or {varName} is replaced by the value of that variable. +# +# 2. The result of the first phase is parsed and run in a similar +# manner to JSP or MASON or PHP: anything within < % ... % > is a +# code block, anything else is sent as output, subject to the +# flow imposed by any code segments. +# +# 3. The "output" is a POSIX shell script, which has its own ideas about +# escaping with backslashes, so to get «\» you need to write «\\\\» +# and to get «$» you need to write «\\\$». +# +# For more details about the Groovy Template Engine, see +# https://docs.groovy-lang.org/next/html/documentation/ section §3.15 +# (Template Engines) for details. +# +# (An example invocation of this template is from +# https://github.com/gradle/gradle/blob/HEAD/subprojects/build-init/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java +# within the Gradle project, which builds "gradlew".) +# */ %> +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME + # Resolve links: \$0 may be a link -PRG="\$0" -# Need this for relative symlinks. -while [ -h "\$PRG" ] ; do - ls=`ls -ld "\$PRG"` - link=`expr "\$ls" : '.*-> \\(.*\\)\$'` - if expr "\$link" : '/.*' > /dev/null; then - PRG="\$link" - else - PRG=`dirname "\$PRG"`"/\$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"\$PRG\"`/${appHomeRelativePath}" >/dev/null -APP_HOME="`pwd -P`" -cd "\$SAVED" >/dev/null +app_path=\$0 -APP_NAME="${applicationName}" -APP_BASE_NAME=`basename "\$0"` +# Need this for daisy-chained symlinks. +while + APP_HOME=\${app_path%"\${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "\$app_path" ] +do + ls=\$( ls -ld "\$app_path" ) + link=\${ls#*' -> '} + case \$link in #( + /*) app_path=\$link ;; #( + *) app_path=\$APP_HOME\$link ;; + esac +done -# Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. -DEFAULT_JVM_OPTS=${defaultJvmOpts} +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=\${0##*/} +# Discard cd standard output in case \$CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=\$( cd -P "\${APP_HOME:-./}${appHomeRelativePath}" > /dev/null && printf '%s\\n' "\$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "\$*" -} +} >&2 die () { echo echo "\$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "\$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$classpath +<% if ( mainClassName.startsWith('--module ') ) { %> +MODULE_PATH=$modulePath +<% } %> # Determine the Java command to use to start the JVM. if [ -n "\$JAVA_HOME" ] ; then if [ -x "\$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="\$JAVA_HOME/jre/sh/java" + JAVACMD=\$JAVA_HOME/jre/sh/java else - JAVACMD="\$JAVA_HOME/bin/java" + JAVACMD=\$JAVA_HOME/bin/java fi if [ ! -x "\$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: \$JAVA_HOME @@ -97,87 +167,126 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. -if [ "\$cygwin" = "false" -a "\$darwin" = "false" -a "\$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ \$? -eq 0 ] ; then - if [ "\$MAX_FD" = "maximum" -o "\$MAX_FD" = "max" ] ; then - MAX_FD="\$MAX_FD_LIMIT" - fi - ulimit -n \$MAX_FD - if [ \$? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: \$MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: \$MAX_FD_LIMIT" - fi +if ! "\$cygwin" && ! "\$darwin" && ! "\$nonstop" ; then + case \$MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=\$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case \$MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "\$MAX_FD" || + warn "Could not set maximum file descriptor limit to \$MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if \$darwin; then - GRADLE_OPTS="\$GRADLE_OPTS \\"-Xdock:name=\$APP_NAME\\" \\"-Xdock:icon=\$APP_HOME/media/gradle.icns\\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and ${optsEnvironmentVar} environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "\$cygwin" = "true" -o "\$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "\$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "\$CLASSPATH"` - JAVACMD=`cygpath --unix "\$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in \$ROOTDIRSRAW ; do - ROOTDIRS="\$ROOTDIRS\$SEP\$dir" - SEP="|" - done - OURCYGPATTERN="(^(\$ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "\$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="\$OURCYGPATTERN|(\$GRADLE_CYGPATTERN)" - fi +if "\$cygwin" || "\$msys" ; then + APP_HOME=\$( cygpath --path --mixed "\$APP_HOME" ) + CLASSPATH=\$( cygpath --path --mixed "\$CLASSPATH" ) +<% if ( mainClassName.startsWith('--module ') ) { %> MODULE_PATH=\$( cygpath --path --mixed "\$MODULE_PATH" )<% } %> + JAVACMD=\$( cygpath --unix "\$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "\$@" ; do - CHECK=`echo "\$arg"|egrep -c "\$OURCYGPATTERN" -` - CHECK2=`echo "\$arg"|egrep -c "^-"` ### Determine if an option - - if [ \$CHECK -ne 0 ] && [ \$CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args\$i`=`cygpath --path --ignore --mixed "\$arg"` - else - eval `echo args\$i`="\"\$arg\"" + for arg do + if + case \$arg in #( + -*) false ;; # don't mess with options #( + /?*) t=\${arg#/} t=/\${t%%/*} # looks like a POSIX filepath + [ -e "\$t" ] ;; #( + *) false ;; + esac + then + arg=\$( cygpath --path --ignore --mixed "\$arg" ) fi - i=`expr \$i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "\$@" "\$arg" # push replacement arg done - case \$i in - 0) set -- ;; - 1) set -- "\$args0" ;; - 2) set -- "\$args0" "\$args1" ;; - 3) set -- "\$args0" "\$args1" "\$args2" ;; - 4) set -- "\$args0" "\$args1" "\$args2" "\$args3" ;; - 5) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" ;; - 6) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" ;; - 7) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" "\$args6" ;; - 8) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" "\$args6" "\$args7" ;; - 9) set -- "\$args0" "\$args1" "\$args2" "\$args3" "\$args4" "\$args5" "\$args6" "\$args7" "\$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\\\n "\$i" | sed "s/'/'\\\\\\\\''/g;1s/^/'/;\\\$s/\\\$/' \\\\\\\\/" ; done - echo " " -} -APP_ARGS=`save "\$@"` +<% /* +# The DEFAULT_JVM_OPTS variable is intentionally defined here to allow using cygwin-processed APP_HOME. +# So far the only way to inject APP_HOME reference into DEFAULT_JVM_OPTS is to post-process the start script; the declaration is a good anchor to do that. +*/ %> +# Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. +DEFAULT_JVM_OPTS=${defaultJvmOpts} + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect \${Hostname} to be expanded, as it is an environment variable and will be +# treated as '\${Hostname}' itself on the command line. + +set -- \\ +<% if ( appNameSystemProperty ) { + %> "-D${appNameSystemProperty}=\$APP_BASE_NAME" \\ +<% } %> -classpath "\$CLASSPATH" \\ +<% if ( mainClassName.startsWith('--module ') ) { + %> --module-path "\$MODULE_PATH" \\ +<% } %> ${mainClassName} \\ + "\$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"\$var" ) && +# set -- "\${ARGS[@]}" "\$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- \$DEFAULT_JVM_OPTS \$JAVA_OPTS \$${optsEnvironmentVar} <% if ( appNameSystemProperty ) { %>"\"-D${appNameSystemProperty}=\$APP_BASE_NAME\"" <% } %>-jar "\"\$CLASSPATH\"" "\$APP_ARGS" +eval "set -- \$( + printf '%s\\n' "\$DEFAULT_JVM_OPTS \$JAVA_OPTS \$${optsEnvironmentVar}" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' | + tr '\\n' ' ' + )" '"\$@"' -exec "\$JAVACMD" "\$@" \ No newline at end of file +exec "\$JAVACMD" "\$@" diff --git a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt index 71f72e7e3..b487fee0e 100644 --- a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt +++ b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt @@ -1,4 +1,22 @@ -@if "%DEBUG%" == "" @echo off +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem ${applicationName} startup script for Windows @@ -9,11 +27,15 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=.\ +if "%DIRNAME%"=="" set DIRNAME=.\ +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME%${appHomeRelativePath} +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + @rem Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. set DEFAULT_JVM_OPTS=${defaultJvmOpts} @@ -22,13 +44,13 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -36,50 +58,38 @@ goto fail set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe -if exist "%JAVA_EXE%" goto init +if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* - :execute @rem Setup the command line set CLASSPATH=$classpath +<% if ( mainClassName.startsWith('--module ') ) { %>set MODULE_PATH=$modulePath<% } %> @rem Execute ${applicationName} -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %> -jar "%CLASSPATH%" %CMD_LINE_ARGS% +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %> -classpath "%CLASSPATH%" <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "%MODULE_PATH%" <% } %>${mainClassName} %* :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable ${exitEnvironmentVar} if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%${exitEnvironmentVar}%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%${exitEnvironmentVar}%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal -:omega \ No newline at end of file +:omega From 7594dfe15d0bca3d593b98beb749c7a0233ab8b1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 24 Jan 2025 09:06:57 -0500 Subject: [PATCH 178/941] Show how to add extra files into distributions (#1185) * Add canAddExtraFilesIntoDistribution * Update src/docs/application-plugin/README.md --- src/docs/application-plugin/README.md | 25 +++++++++++++++ .../plugins/shadow/ApplicationPluginTest.kt | 31 +++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/src/docs/application-plugin/README.md b/src/docs/application-plugin/README.md index 2ce520a96..5534c4ae3 100644 --- a/src/docs/application-plugin/README.md +++ b/src/docs/application-plugin/README.md @@ -55,3 +55,28 @@ the application. Additionally, the plugin will create the `installShadowDist` and `startShadowScripts` tasks which stages the necessary files for a distribution to `build/install/-shadow/`. + +You can also add more files into the distribution like: + +```groovy +// Add extra files to the distribution +plugins { + id 'java' + id 'application' + id 'com.gradleup.shadow' +} + +application { + mainClass = 'myapp.Main' +} + +// `shadow` is the name of the distribution created by Shadow plugin +distributions.named('shadow') { + contents.into('extra') { + from project.file('extra/echo.sh') + } +} +``` + +View [the official doc described](https://docs.gradle.org/current/userguide/distribution_plugin.html#distribution_plugin) +for more information about configuring distributions. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 442da2e25..2e3e8888e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -6,9 +6,11 @@ import assertk.assertions.containsOnly import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.DISTRIBUTION_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.getContent import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.isWindows import com.github.jengelman.gradle.plugins.shadow.util.runProcess @@ -190,6 +192,35 @@ class ApplicationPluginTest : BasePluginTest() { ) } + @Test + fun canAddExtraFilesIntoDistribution() { + path("extra/echo.sh").writeText("echo 'Hello, World!'") + prepare( + projectBlock = """ + distributions.named('$DISTRIBUTION_NAME') { + contents.into('extra') { + from project.file('extra/echo.sh') + } + } + """.trimIndent(), + ) + + run("shadowDistZip") + + val zipPath = path("build/distributions/myapp-shadow-1.0.zip") + ZipFile(zipPath.toFile()).use { zip -> + val entries = zip.entries().toList().filter { !it.isDirectory }.map { it.name } + assertThat(entries).containsOnly( + "myapp-shadow-1.0/bin/myapp", + "myapp-shadow-1.0/bin/myapp.bat", + "myapp-shadow-1.0/lib/myapp-1.0-all.jar", + "myapp-shadow-1.0/extra/echo.sh", + ) + assertThat(zip.getContent("myapp-shadow-1.0/extra/echo.sh")) + .isEqualTo("echo 'Hello, World!'") + } + } + private fun prepare( mainClassWithImports: Boolean = false, projectBlock: String = "", From 7c76ebc0a12cbb1ae36fa926f8d17815750fbb9f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 24 Jan 2025 14:24:20 +0000 Subject: [PATCH 179/941] Update dependency gradle to v8.12.1 (#1186) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a793a..e18bc253b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 3818ce626c7df204ede7d54b8b5d5207bd5a8288 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 24 Jan 2025 09:40:27 -0500 Subject: [PATCH 180/941] Saving configuration-cache data on CI (#1187) https://github.com/gradle/actions/blob/main/docs/setup-gradle.md#saving-configuration-cache-data --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38b7e36f3..e0847ff14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,6 +22,8 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java }} - uses: gradle/actions/setup-gradle@v4 + with: + cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - run: ./gradlew build publish-snapshot: From 42b4280b50b6a330fe7d65aac6a684d24841cade Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 24 Jan 2025 22:06:09 -0500 Subject: [PATCH 181/941] Declare manifest attributes in sample task (#1189) * More checks in canRegisterCustomShadowJarTask * Update src/docs/custom-tasks/README.md * Cleanup src/$sourceSet/java/shadow/$className.java --- src/docs/custom-tasks/README.md | 8 ++++++- .../gradle/plugins/shadow/BasePluginTest.kt | 9 +++++--- .../gradle/plugins/shadow/JavaPluginTest.kt | 21 +++++++++++++++++-- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/src/docs/custom-tasks/README.md b/src/docs/custom-tasks/README.md index 23559d03c..bbb7a0ebd 100644 --- a/src/docs/custom-tasks/README.md +++ b/src/docs/custom-tasks/README.md @@ -14,9 +14,15 @@ def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle. archiveClassifier = "tests" from sourceSets.test.output configurations = [project.configurations.testRuntimeClasspath] + + manifest { + // Optionally, set the main class for the JAR. + attributes 'Main-Class': 'test.Main' + // You can also set other attributes here. + } } -// Optionally, make the `assemble` task depend on the new task +// Optionally, make the `assemble` task depend on the new task. tasks.named('assemble') { dependsOn testShadowJar } 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 a7c139d15..795187f6b 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 @@ -159,19 +159,22 @@ abstract class BasePluginTest { } fun writeMainClass( + sourceSet: String = "main", withImports: Boolean = false, className: String = "Main", ) { val imports = if (withImports) "import junit.framework.Test;" else "" val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" - - path("src/main/java/shadow/$className.java").writeText( + path("src/$sourceSet/java/shadow/$className.java").writeText( """ package shadow; $imports public class $className { public static void main(String[] args) { - String content = String.format("Hello, World! (%s) from $className", 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); } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 001ff5821..64eab5d74 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -18,6 +18,7 @@ import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.isRegular +import com.github.jengelman.gradle.plugins.shadow.util.runProcess import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText @@ -491,6 +492,7 @@ class JavaPluginTest : BasePluginTest() { ) @Test fun canRegisterCustomShadowJarTask() { + writeMainClass(sourceSet = "test", withImports = true) val testShadowJarTask = "testShadowJar" projectScriptPath.appendText( """ @@ -503,6 +505,9 @@ class JavaPluginTest : BasePluginTest() { archiveClassifier = "tests" from sourceSets.test.output configurations = [project.configurations.testRuntimeClasspath] + manifest { + attributes 'Main-Class': 'shadow.Main' + } } """.trimIndent(), ) @@ -510,8 +515,20 @@ class JavaPluginTest : BasePluginTest() { val result = run(testShadowJarTask) assertThat(result).taskOutcomeEquals(":$testShadowJarTask", SUCCESS) - val junitEntry = jarPath("build/libs/shadow-1.0-tests.jar").use { it.getEntry("junit") } - assertThat(junitEntry).isNotNull() + assertThat(jarPath("build/libs/shadow-1.0-tests.jar")).useAll { + containsEntries( + "junit/framework/Test.class", + "shadow/Main.class", + ) + getMainAttr("Main-Class").isNotNull() + } + + val pathString = path("build/libs/shadow-1.0-tests.jar").toString() + val runningOutput = runProcess("java", "-jar", pathString, "foo") + assertThat(runningOutput).contains( + "Hello, World! (foo) from Main", + "Refs: junit.framework.Test", + ) } @Test From d738e61e00551da37c0dd982db7295c9d3ff537a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 25 Jan 2025 07:01:42 -0500 Subject: [PATCH 182/941] Test publish jars that depend on shadow jars (#1190) * Replace writeShadowedClientAndServerModules * Write client version * Add publishJarThatDependsOnShadowJar * Renames --- .../gradle/plugins/shadow/BasePluginTest.kt | 23 +++++- .../gradle/plugins/shadow/JavaPluginTest.kt | 31 ++------ .../gradle/plugins/shadow/PublishingTest.kt | 72 +++++++++++++++++++ .../shadow/util/GradleModuleMetadata.kt | 14 ++++ 4 files changed, 112 insertions(+), 28 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 795187f6b..536277fcb 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 @@ -184,6 +184,7 @@ abstract class BasePluginTest { } fun writeClientAndServerModules( + clientShadowed: Boolean = false, serverShadowBlock: String = "", ) { settingsScriptPath.appendText( @@ -201,7 +202,7 @@ abstract class BasePluginTest { ) path("client/build.gradle").writeText( """ - ${getDefaultProjectBuildScript("java")} + ${getDefaultProjectBuildScript("java", withVersion = true)} dependencies { implementation 'junit:junit:3.8.2' } @@ -226,6 +227,26 @@ abstract class BasePluginTest { } """.trimIndent() + System.lineSeparator(), ) + + if (!clientShadowed) return + path("client/build.gradle").appendText( + """ + $shadowJar { + relocate 'junit.framework', 'client.junit.framework' + } + """.trimIndent() + System.lineSeparator(), + ) + path("server/src/main/java/server/Server.java").writeText( + """ + package server; + import client.Client; + import client.junit.framework.Test; + public class Server {} + """.trimIndent(), + ) + val replaced = path("server/build.gradle").readText() + .replace("project(':client')", "project(path: ':client', configuration: 'shadow')") + path("server/build.gradle").writeText(replaced) } fun writeGradlePluginModule(legacy: Boolean) { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 64eab5d74..e52d839dd 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -20,7 +20,6 @@ import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.isRegular import com.github.jengelman.gradle.plugins.shadow.util.runProcess import kotlin.io.path.appendText -import kotlin.io.path.readText import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin import org.gradle.testfixtures.ProjectBuilder @@ -152,7 +151,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun dependOnProjectShadowJar() { - writeShadowedClientAndServerModules() + writeClientAndServerModules(clientShadowed = true) run(":server:jar") @@ -166,7 +165,7 @@ class JavaPluginTest : BasePluginTest() { "client/junit/framework/Test.class", ) } - assertThat(jarPath("client/build/libs/client-all.jar")).useAll { + assertThat(jarPath("client/build/libs/client-1.0-all.jar")).useAll { containsEntries( "client/Client.class", "client/junit/framework/Test.class", @@ -176,7 +175,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun shadowProjectShadowJar() { - writeShadowedClientAndServerModules() + writeClientAndServerModules(clientShadowed = true) run(serverShadowJarTask) @@ -190,7 +189,7 @@ class JavaPluginTest : BasePluginTest() { "junit/framework/Test.class", ) } - assertThat(jarPath("client/build/libs/client-all.jar")).useAll { + assertThat(jarPath("client/build/libs/client-1.0-all.jar")).useAll { containsEntries( "client/Client.class", "client/junit/framework/Test.class", @@ -616,26 +615,4 @@ class JavaPluginTest : BasePluginTest() { ) } } - - private fun writeShadowedClientAndServerModules() { - writeClientAndServerModules() - path("client/build.gradle").appendText( - """ - $shadowJar { - relocate 'junit.framework', 'client.junit.framework' - } - """.trimIndent(), - ) - path("server/src/main/java/server/Server.java").writeText( - """ - package server; - import client.Client; - import client.junit.framework.Test; - public class Server {} - """.trimIndent(), - ) - val replaced = path("server/build.gradle").readText() - .replace("project(':client')", "project(path: ':client', configuration: 'shadow')") - path("server/build.gradle").writeText(replaced) - } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 7b60d8233..968ba2ee5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -6,6 +6,7 @@ import assertk.assertions.contains import assertk.assertions.containsOnly import assertk.assertions.isEmpty import assertk.assertions.isEqualTo +import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata @@ -205,6 +206,77 @@ class PublishingTest : BasePluginTest() { assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) } + @Test + fun publishJarThatDependsOnShadowJar() { + writeClientAndServerModules(clientShadowed = true) + path("client/build.gradle").appendText( + """ + apply plugin: 'maven-publish' + group = 'example' + publishing { + publications { + shadow(MavenPublication) { + from components.shadow + } + } + repositories { + maven { + url = '${remoteRepoPath.toUri()}' + } + } + } + """.trimIndent(), + ) + path("server/build.gradle").appendText( + """ + apply plugin: 'maven-publish' + group = 'example' + publishing { + publications { + java(MavenPublication) { + from components.java + } + } + repositories { + maven { + url = '${remoteRepoPath.toUri()}' + } + } + } + """.trimIndent(), + ) + + publish() + + gmmAdapter.fromJson(repoPath("example/server/1.0/server-1.0.module")).let { gmm -> + assertThat(gmm.variants.map { it.name }).containsOnly( + API_ELEMENTS_CONFIGURATION_NAME, + RUNTIME_ELEMENTS_CONFIGURATION_NAME, + SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, + ) + val runtimeDependencies = gmm.variants.single { it.name == RUNTIME_ELEMENTS_CONFIGURATION_NAME }.depStrings + assertThat(runtimeDependencies).containsOnly( + "example:client:1.0", + ) + val shadowDependencies = gmm.variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME }.depStrings + assertThat(shadowDependencies).isEmpty() + } + gmmAdapter.fromJson(repoPath("example/client/1.0/client-1.0.module")).let { gmm -> + assertThat(gmm.variants.map { it.name }).containsOnly( + SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, + ) + assertThat(gmm.variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME }).all { + transform { it.attributes }.all { + contains(Category.CATEGORY_ATTRIBUTE.name, Category.LIBRARY) + contains(Bundling.BUNDLING_ATTRIBUTE.name, Bundling.SHADOWED) + contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name, LibraryElements.JAR) + contains(Usage.USAGE_ATTRIBUTE.name, Usage.JAVA_RUNTIME) + } + transform { it.fileNames }.single().isEqualTo("client-1.0-all.jar") + } + } + } + @Test fun publishShadowJarWithGradleMetadata() { projectScriptPath.appendText( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt index 13c95b32a..232763eed 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt @@ -10,9 +10,23 @@ data class GradleModuleMetadata( val name: String, val attributes: Map, val dependencies: List = emptyList(), + val files: List = emptyList(), ) { + val depStrings: List get() = dependencies.map { it.coordinate } + val fileNames: List get() = files.map { it.name } + data class Dependency( + val group: String, val module: String, + val version: Version, + ) { + val coordinate: String get() = "$group:$module:${version.requires}" + + data class Version(val requires: String) + } + + data class File( + val name: String, ) } } From 8526847fbe31464c772a51685192a3c2932ebf95 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 25 Jan 2025 09:31:36 -0500 Subject: [PATCH 183/941] More robust PublishingTest (#1191) * Move gmmAdapter and pomReader into companion object * Reuse assertShadowVariantCommon * Private properties in GradleModuleMetadata * Rename depStrings to gavs * More tweaks in GradleModuleMetadata * Add gav extensions for maven model * Reuse publishingBlock * Replace contains with containsAtLeast * Remove private * Reuse commonVariantAttrs * Replace containsAtLeast with containsOnly * Remove TODO * Use more assertShadowVariantCommon * Note targetCompatibility usage --- .../gradle/plugins/shadow/PublishingTest.kt | 205 +++++++++--------- .../shadow/util/AppendableMavenRepository.kt | 6 + .../shadow/util/GradleModuleMetadata.kt | 19 +- 3 files changed, 122 insertions(+), 108 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 968ba2ee5..35ea98185 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -1,8 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow +import assertk.Assert import assertk.all import assertk.assertThat -import assertk.assertions.contains import assertk.assertions.containsOnly import assertk.assertions.isEmpty import assertk.assertions.isEqualTo @@ -14,6 +14,7 @@ import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.gavs import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi @@ -28,10 +29,12 @@ import kotlin.io.path.readText import kotlin.io.path.writeText import org.apache.maven.model.Model import org.apache.maven.model.io.xpp3.MavenXpp3Reader +import org.gradle.api.JavaVersion import org.gradle.api.attributes.Bundling import org.gradle.api.attributes.Category import org.gradle.api.attributes.LibraryElements import org.gradle.api.attributes.Usage +import org.gradle.api.attributes.java.TargetJvmVersion import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME import org.gradle.testkit.runner.BuildResult @@ -40,10 +43,6 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir class PublishingTest : BasePluginTest() { - private val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() - private val gmmAdapter = moshi.adapter(GradleModuleMetadata::class.java) - private val pomReader = MavenXpp3Reader() - @TempDir lateinit var remoteRepoPath: Path @@ -68,6 +67,7 @@ class PublishingTest : BasePluginTest() { assertShadowJarCommon(repoJarPath("shadow/maven-all/1.0/maven-all-1.0.jar")) assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven-all/1.0/maven-all-1.0.module"))) } @Test @@ -148,6 +148,7 @@ class PublishingTest : BasePluginTest() { assertShadowJarCommon(repoJarPath("shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext")) assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven-all/1.0/maven-all-1.0.module"))) } @Test @@ -204,74 +205,54 @@ class PublishingTest : BasePluginTest() { ) } assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven-all/1.0/maven-all-1.0.module"))) } @Test fun publishJarThatDependsOnShadowJar() { writeClientAndServerModules(clientShadowed = true) path("client/build.gradle").appendText( - """ - apply plugin: 'maven-publish' - group = 'example' - publishing { - publications { - shadow(MavenPublication) { - from components.shadow - } - } - repositories { - maven { - url = '${remoteRepoPath.toUri()}' - } + publishingBlock( + projectBlock = "group = 'example'", + publicationsBlock = """ + shadow(MavenPublication) { + from components.shadow } - } - """.trimIndent(), + """.trimIndent(), + ), ) path("server/build.gradle").appendText( - """ - apply plugin: 'maven-publish' - group = 'example' - publishing { - publications { - java(MavenPublication) { - from components.java - } - } - repositories { - maven { - url = '${remoteRepoPath.toUri()}' - } + publishingBlock( + projectBlock = "group = 'example'", + publicationsBlock = """ + java(MavenPublication) { + from components.java } - } - """.trimIndent(), + """.trimIndent(), + ), ) publish() gmmAdapter.fromJson(repoPath("example/server/1.0/server-1.0.module")).let { gmm -> - assertThat(gmm.variants.map { it.name }).containsOnly( + assertThat(gmm.variantNames).containsOnly( API_ELEMENTS_CONFIGURATION_NAME, RUNTIME_ELEMENTS_CONFIGURATION_NAME, SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) - val runtimeDependencies = gmm.variants.single { it.name == RUNTIME_ELEMENTS_CONFIGURATION_NAME }.depStrings - assertThat(runtimeDependencies).containsOnly( + assertThat(gmm.runtimeElementsVariant.gavs).containsOnly( "example:client:1.0", ) - val shadowDependencies = gmm.variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME }.depStrings - assertThat(shadowDependencies).isEmpty() + assertThat(gmm.shadowRuntimeElementsVariant.gavs).isEmpty() + assertShadowVariantCommon(gmm, gavs = emptyArray()) { + transform { it.fileNames }.single().isEqualTo("server-1.0-all.jar") + } } gmmAdapter.fromJson(repoPath("example/client/1.0/client-1.0.module")).let { gmm -> - assertThat(gmm.variants.map { it.name }).containsOnly( + assertThat(gmm.variantNames).containsOnly( SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) - assertThat(gmm.variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME }).all { - transform { it.attributes }.all { - contains(Category.CATEGORY_ATTRIBUTE.name, Category.LIBRARY) - contains(Bundling.BUNDLING_ATTRIBUTE.name, Bundling.SHADOWED) - contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name, LibraryElements.JAR) - contains(Usage.USAGE_ATTRIBUTE.name, Usage.JAVA_RUNTIME) - } + assertShadowVariantCommon(gmm, gavs = emptyArray()) { transform { it.fileNames }.single().isEqualTo("client-1.0-all.jar") } } @@ -284,6 +265,10 @@ class PublishingTest : BasePluginTest() { projectBlock = """ group = 'com.acme' version = '1.0' + java { + // This is necessary for `org.gradle.jvm.version` to be pinned on Java 8 for `components.java` MavenPublication. + targetCompatibility = JavaVersion.VERSION_1_8 + } """.trimIndent(), dependenciesBlock = """ implementation 'shadow:a:1.0' @@ -312,50 +297,41 @@ class PublishingTest : BasePluginTest() { containsEntries(*entries) } - pomReader.read(repoPath("com/acme/maven/1.0/maven-1.0.pom")).let { pom -> - assertThat(pom.dependencies.size).isEqualTo(2) - pom.dependencies[0].let { dependency -> - assertThat(dependency.groupId).isEqualTo("shadow") - assertThat(dependency.artifactId).isEqualTo("a") - assertThat(dependency.version).isEqualTo("1.0") - } - pom.dependencies[1].let { dependency -> - assertThat(dependency.groupId).isEqualTo("shadow") - assertThat(dependency.artifactId).isEqualTo("b") - assertThat(dependency.version).isEqualTo("1.0") - } - } + assertPomCommon(repoPath("com/acme/maven/1.0/maven-1.0.pom"), arrayOf("shadow:a:1.0", "shadow:b:1.0")) gmmAdapter.fromJson(repoPath("com/acme/maven/1.0/maven-1.0.module")).let { gmm -> // apiElements, runtimeElements, shadowRuntimeElements - assertThat(gmm.variants.map { it.name }).containsOnly( + assertThat(gmm.variantNames).containsOnly( API_ELEMENTS_CONFIGURATION_NAME, RUNTIME_ELEMENTS_CONFIGURATION_NAME, SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) - assertThat(gmm.variants.single { it.name == API_ELEMENTS_CONFIGURATION_NAME }).all { - transform { it.attributes }.all { - contains(Category.CATEGORY_ATTRIBUTE.name, Category.LIBRARY) - contains(Bundling.BUNDLING_ATTRIBUTE.name, Bundling.EXTERNAL) - contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name, LibraryElements.JAR) - contains(Usage.USAGE_ATTRIBUTE.name, Usage.JAVA_API) - } - transform { it.dependencies }.isEmpty() + assertThat(gmm.apiElementsVariant).all { + transform { it.attributes }.containsOnly( + *commonVariantAttrs, + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_API, + TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to JavaVersion.VERSION_1_8.majorVersion, + ) + transform { it.gavs }.isEmpty() } - assertThat(gmm.variants.single { it.name == RUNTIME_ELEMENTS_CONFIGURATION_NAME }).all { - transform { it.attributes }.all { - contains(Category.CATEGORY_ATTRIBUTE.name, Category.LIBRARY) - contains(Bundling.BUNDLING_ATTRIBUTE.name, Bundling.EXTERNAL) - contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name, LibraryElements.JAR) - contains(Usage.USAGE_ATTRIBUTE.name, Usage.JAVA_RUNTIME) - } - transform { it.dependencies.map { dep -> dep.module } }.containsOnly("a", "b") + assertThat(gmm.runtimeElementsVariant).all { + transform { it.attributes }.containsOnly( + *commonVariantAttrs, + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to JavaVersion.VERSION_1_8.majorVersion, + ) + transform { it.gavs }.containsOnly( + "shadow:a:1.0", + "shadow:b:1.0", + ) } assertShadowVariantCommon(gmm) } assertPomCommon(repoPath("com/acme/maven-all/1.0/maven-all-1.0.pom")) gmmAdapter.fromJson(repoPath("com/acme/maven-all/1.0/maven-all-1.0.module")).let { gmm -> - assertThat(gmm.variants.map { it.name }).containsOnly( + assertThat(gmm.variantNames).containsOnly( SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) assertShadowVariantCommon(gmm) @@ -389,44 +365,56 @@ class PublishingTest : BasePluginTest() { """.trimIndent(), ): String { return """ - apply plugin: 'maven-publish' - $projectBlock dependencies { $dependenciesBlock } $shadowJar { $shadowBlock } - publishing { - publications { - $publicationsBlock - } - repositories { - maven { - url = '${remoteRepoPath.toUri()}' - } + ${publishingBlock(projectBlock = projectBlock, publicationsBlock = publicationsBlock)} + """.trimIndent() + } + + private fun publishingBlock( + projectBlock: String, + publicationsBlock: String, + ): String { + return """ + apply plugin: 'maven-publish' + $projectBlock + publishing { + publications { + $publicationsBlock + } + repositories { + maven { + url = '${remoteRepoPath.toUri()}' } } + } """.trimIndent() } - private fun assertPomCommon(pomPath: Path) { - val pom = pomReader.read(pomPath) - val dependency = pom.dependencies.single() - assertThat(dependency.groupId).isEqualTo("shadow") - assertThat(dependency.artifactId).isEqualTo("b") - assertThat(dependency.version).isEqualTo("1.0") + private fun assertPomCommon( + pomPath: Path, + gavs: Array = arrayOf("shadow:b:1.0"), + ) { + assertThat(pomReader.read(pomPath).gavs).containsOnly(*gavs) } - private fun assertShadowVariantCommon(gmm: GradleModuleMetadata) { - assertThat(gmm.variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME }).all { - transform { it.attributes }.all { - contains(Category.CATEGORY_ATTRIBUTE.name, Category.LIBRARY) - contains(Bundling.BUNDLING_ATTRIBUTE.name, Bundling.SHADOWED) - contains(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name, LibraryElements.JAR) - contains(Usage.USAGE_ATTRIBUTE.name, Usage.JAVA_RUNTIME) - } - transform { it.dependencies.map { dep -> dep.module } }.containsOnly("b") + private fun assertShadowVariantCommon( + gmm: GradleModuleMetadata, + gavs: Array = arrayOf("shadow:b:1.0"), + body: Assert.() -> Unit = {}, + ) { + assertThat(gmm.shadowRuntimeElementsVariant).all { + transform { it.attributes }.containsOnly( + *commonVariantAttrs, + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.SHADOWED, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + ) + transform { it.gavs }.containsOnly(*gavs) + body() } } @@ -444,6 +432,15 @@ class PublishingTest : BasePluginTest() { } private companion object { + val gmmAdapter: JsonAdapter = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() + .adapter(GradleModuleMetadata::class.java) + val pomReader = MavenXpp3Reader() + + val commonVariantAttrs = arrayOf( + Category.CATEGORY_ATTRIBUTE.name to Category.LIBRARY, + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name to LibraryElements.JAR, + ) + fun MavenXpp3Reader.read(path: Path): Model = path.inputStream().use { read(it) } fun JsonAdapter.fromJson(path: Path): T = requireNotNull(fromJson(path.readText())) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index a9764473b..f4956cde3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -10,6 +10,7 @@ import kotlin.io.path.name import kotlin.io.path.writeText import org.apache.maven.model.Dependency import org.apache.maven.model.Model +import org.apache.maven.model.ModelBase import org.gradle.testkit.runner.GradleRunner class AppendableMavenRepository( @@ -131,3 +132,8 @@ class AppendableMavenRepository( } } } + +val Dependency.gav: String get() = "$groupId:$artifactId:$version" + +@Suppress("SpellCheckingInspection") +val ModelBase.gavs: List get() = dependencies.map { it.gav } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt index 232763eed..08e305122 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt @@ -1,18 +1,29 @@ package com.github.jengelman.gradle.plugins.shadow.util +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME + /** * Refs the format from [Gradle Module Metadata](https://github.com/gradle/gradle/blob/master/platforms/documentation/docs/src/docs/design/gradle-module-metadata-latest-specification.md). */ data class GradleModuleMetadata( - val variants: List, + private val variants: List, ) { + val apiElementsVariant: Variant get() = variants.single { it.name == API_ELEMENTS_CONFIGURATION_NAME } + val runtimeElementsVariant: Variant get() = variants.single { it.name == RUNTIME_ELEMENTS_CONFIGURATION_NAME } + val shadowRuntimeElementsVariant: Variant get() = variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME } + + val variantNames: List get() = variants.map { it.name } + data class Variant( val name: String, val attributes: Map, - val dependencies: List = emptyList(), - val files: List = emptyList(), + private val dependencies: List = emptyList(), + private val files: List = emptyList(), ) { - val depStrings: List get() = dependencies.map { it.coordinate } + @Suppress("SpellCheckingInspection") + val gavs: List get() = dependencies.map { it.coordinate } val fileNames: List get() = files.map { it.name } data class Dependency( From 5fd14788626a32656b772d19c7ab800287e3fbe8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 25 Jan 2025 22:37:22 +0800 Subject: [PATCH 184/941] Bump @babel/traverse from 7.22.10 to 7.26.7 (#1192) Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.10 to 7.26.7. - [Release notes](https://github.com/babel/babel/releases) - [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md) - [Commits](https://github.com/babel/babel/commits/v7.26.7/packages/babel-traverse) --- updated-dependencies: - dependency-name: "@babel/traverse" dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 113 +++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 99 insertions(+), 14 deletions(-) diff --git a/yarn.lock b/yarn.lock index 4b4d281f5..597cac2ef 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18,6 +18,15 @@ "@babel/highlight" "^7.22.10" chalk "^2.4.2" +"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.2": + version "7.26.2" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" + integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== + dependencies: + "@babel/helper-validator-identifier" "^7.25.9" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" @@ -54,6 +63,17 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" +"@babel/generator@^7.26.5": + version "7.26.5" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.5.tgz#e44d4ab3176bbcaf78a5725da5f1dc28802a9458" + integrity sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw== + dependencies: + "@babel/parser" "^7.26.5" + "@babel/types" "^7.26.5" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^3.0.2" + "@babel/helper-annotate-as-pure@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" @@ -215,11 +235,21 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" + integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== + "@babel/helper-validator-identifier@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== +"@babel/helper-validator-identifier@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" + integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== + "@babel/helper-validator-option@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" @@ -257,6 +287,13 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== +"@babel/parser@^7.25.9", "@babel/parser@^7.26.5", "@babel/parser@^7.26.7": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.7.tgz#e114cd099e5f7d17b05368678da0fb9f69b3385c" + integrity sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w== + dependencies: + "@babel/types" "^7.26.7" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" @@ -954,20 +991,26 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/traverse@^7.22.10", "@babel/traverse@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" - integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== +"@babel/template@^7.25.9": + version "7.25.9" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" + integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== dependencies: - "@babel/code-frame" "^7.22.10" - "@babel/generator" "^7.22.10" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.10" - "@babel/types" "^7.22.10" - debug "^4.1.0" + "@babel/code-frame" "^7.25.9" + "@babel/parser" "^7.25.9" + "@babel/types" "^7.25.9" + +"@babel/traverse@^7.22.10", "@babel/traverse@^7.22.5": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.7.tgz#99a0a136f6a75e7fb8b0a1ace421e0b25994b8bb" + integrity sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA== + dependencies: + "@babel/code-frame" "^7.26.2" + "@babel/generator" "^7.26.5" + "@babel/parser" "^7.26.7" + "@babel/template" "^7.25.9" + "@babel/types" "^7.26.7" + debug "^4.3.1" globals "^11.1.0" "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.4.4": @@ -979,6 +1022,14 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" +"@babel/types@^7.25.9", "@babel/types@^7.26.5", "@babel/types@^7.26.7": + version "7.26.7" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.7.tgz#5e2b89c0768e874d4d061961f3a5a153d71dc17a" + integrity sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg== + dependencies: + "@babel/helper-string-parser" "^7.25.9" + "@babel/helper-validator-identifier" "^7.25.9" + "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" @@ -988,6 +1039,15 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.8" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" + integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" @@ -998,6 +1058,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" @@ -1011,6 +1076,14 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -3127,6 +3200,13 @@ debug@^4.1.0, debug@^4.1.1: dependencies: ms "2.1.2" +debug@^4.3.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" + integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== + dependencies: + ms "^2.1.3" + debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -5201,6 +5281,11 @@ jsesc@^2.5.1: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== +jsesc@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" + integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== + jsesc@~0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" @@ -5757,7 +5842,7 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@2.1.3, ms@^2.1.1: +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== From a9b6f46c7472998b5fe906da345745d36d059e58 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 25 Jan 2025 09:48:43 -0500 Subject: [PATCH 185/941] Configure dependabot (#1193) * Create dependabot.yml * Ignore yarn.lock for workflows --- .github/dependabot.yml | 11 +++++++++++ .github/workflows/ci.yml | 4 ++++ 2 files changed, 15 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..5f0889ce9 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for all configuration options: +# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file + +version: 2 +updates: + - package-ecosystem: "npm" # See documentation for possible values + directory: "/" # Location of package manifests + schedule: + interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0847ff14..3c02d78b7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,11 @@ on: push: branches: - main + paths-ignore: + - yarn.lock pull_request: + paths-ignore: + - yarn.lock workflow_dispatch: jobs: From cba99bbb6da2c76378e3f47c6515d6c1eaf3fca4 Mon Sep 17 00:00:00 2001 From: Simon Marquis Date: Sun, 26 Jan 2025 00:46:48 +0000 Subject: [PATCH 186/941] Move `title` configuration in `config.js` to fix default HTML title (#1194) See `view-source:https://gradleup.com/shadow/` Docs: https://v1.vuepress.vuejs.org/config/#title --- src/docs/.vuepress/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/docs/.vuepress/config.js b/src/docs/.vuepress/config.js index afcab03db..5e6b4b157 100644 --- a/src/docs/.vuepress/config.js +++ b/src/docs/.vuepress/config.js @@ -2,6 +2,7 @@ module.exports = { base: "/shadow/", dest: "build/site", ga: "UA-321220-4", + title: 'Gradle Shadow Plugin', themeConfig: { repo: "GradleUp/shadow", docsBranch: 'main', @@ -9,7 +10,6 @@ module.exports = { editLinkText: 'Help improve these docs!', logo: '/logo+type.svg', docsDir: 'src/docs', - title: 'Gradle Shadow Plugin', nav: [ { text: 'User Guide', link: '/introduction/' } ], From 28d04311f81431cb4306335f458bd404fbb545a3 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 26 Jan 2025 09:19:18 +0800 Subject: [PATCH 187/941] Revert "Saving configuration-cache data on CI (#1187)" This reverts commit 3818ce626c7df204ede7d54b8b5d5207bd5a8288. --- .github/workflows/ci.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3c02d78b7..086ebbc43 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,8 +26,6 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java }} - uses: gradle/actions/setup-gradle@v4 - with: - cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} - run: ./gradlew build publish-snapshot: From 43142e681e63a4c8d6fae2e11b69d11896d53693 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 26 Jan 2025 12:15:38 +0800 Subject: [PATCH 188/941] Add dependencies label for renovate PRs --- .github/renovate.json5 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index e3eb18c10..5a9b54152 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -3,4 +3,7 @@ extends: [ 'config:recommended', ], + "labels": [ + "dependencies", + ], } From 5708e1a126b0c8666127f93071e3d0160565c6d5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 26 Jan 2025 00:43:01 -0500 Subject: [PATCH 189/941] Verify DEFAULT_JVM_OPTS in start scripts (#1195) * Check applicationDefaultJvmArgs in start scripts * Update src/docs/application-plugin/README.md --- src/docs/application-plugin/README.md | 2 ++ .../gradle/plugins/shadow/ApplicationPluginTest.kt | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/docs/application-plugin/README.md b/src/docs/application-plugin/README.md index 5534c4ae3..442f5da01 100644 --- a/src/docs/application-plugin/README.md +++ b/src/docs/application-plugin/README.md @@ -68,6 +68,8 @@ plugins { application { mainClass = 'myapp.Main' + // Optionally, you can add default JVM arguments to the start scripts like this: + applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] } // `shadow` is the name of the distribution created by Shadow plugin diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 2e3e8888e..cc9a0e0db 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -40,6 +40,9 @@ class ApplicationPluginTest : BasePluginTest() { toolchain.languageVersion = JavaLanguageVersion.of(17) } """.trimIndent(), + applicationBlock = """ + applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] + """.trimIndent(), settingsBlock = """ plugins { id 'org.gradle.toolchains.foojay-resolver-convention' @@ -78,9 +81,11 @@ class ApplicationPluginTest : BasePluginTest() { assertThat(unixScript.readText()).contains( "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", "exec \"\$JAVACMD\" \"\$@\"", + "DEFAULT_JVM_OPTS='\"--add-opens=java.base/java.lang=ALL-UNNAMED\"'", ) assertThat(winScript.readText()).contains( "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", + "set DEFAULT_JVM_OPTS=\"--add-opens=java.base/java.lang=ALL-UNNAMED\"", ) val runningOutput = if (isWindows) { @@ -98,6 +103,9 @@ class ApplicationPluginTest : BasePluginTest() { fun shadowApplicationDistributionsShouldUseShadowJar() { prepare( mainClassWithImports = true, + applicationBlock = """ + applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] + """.trimIndent(), dependenciesBlock = "shadow 'junit:junit:3.8.2'", ) @@ -137,9 +145,11 @@ class ApplicationPluginTest : BasePluginTest() { assertThat(unixScript.readText()).contains( "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", "exec \"\$JAVACMD\" \"\$@\"", + "DEFAULT_JVM_OPTS='\"--add-opens=java.base/java.lang=ALL-UNNAMED\"'", ) assertThat(winScript.readText()).contains( "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", + "set DEFAULT_JVM_OPTS=\"--add-opens=java.base/java.lang=ALL-UNNAMED\"", ) val runningOutput = if (isWindows) { @@ -224,6 +234,7 @@ class ApplicationPluginTest : BasePluginTest() { private fun prepare( mainClassWithImports: Boolean = false, projectBlock: String = "", + applicationBlock: String = "", settingsBlock: String = "", dependenciesBlock: String = "implementation 'shadow:a:1.0'", runShadowBlock: String = "", @@ -235,6 +246,7 @@ class ApplicationPluginTest : BasePluginTest() { $projectBlock application { mainClass = 'shadow.Main' + $applicationBlock } dependencies { $dependenciesBlock From 4850db3f3a9c5755d9d38a43b9337cea0838c4ba Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 26 Jan 2025 00:49:08 -0500 Subject: [PATCH 190/941] Remove some test function suffixes (#1196) --- .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 35ea98185..f2fad9d53 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -53,7 +53,7 @@ class PublishingTest : BasePluginTest() { } @Test - fun publishShadowJarWithMavenPublishPlugin() { + fun publishShadowJar() { projectScriptPath.appendText( publishConfiguration( shadowBlock = """ @@ -71,7 +71,7 @@ class PublishingTest : BasePluginTest() { } @Test - fun publishShadowJarInsteadOfJarWithMavenPublishPlugin() { + fun publishShadowJarInsteadOfJar() { projectScriptPath.appendText( publishConfiguration( shadowBlock = """ @@ -93,7 +93,7 @@ class PublishingTest : BasePluginTest() { } @BooleanParameterizedTest - fun publishShadowedGradlePluginWithMavenPublishPlugin(legacy: Boolean) { + fun publishShadowedGradlePlugin(legacy: Boolean) { writeGradlePluginModule(legacy) projectScriptPath.appendText( publishConfiguration( @@ -152,7 +152,7 @@ class PublishingTest : BasePluginTest() { } @Test - fun publishMultiProjectShadowJarWithMavenPublishPlugin() { + fun publishMultiProjectShadowJar() { settingsScriptPath.appendText( """ include 'a', 'b', 'c' From 93004d08ecd47e843047cdc0088190f63ce8d0df Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 26 Jan 2025 04:39:58 -0500 Subject: [PATCH 191/941] Inject TargetJvmVersion attr for GMM (#1199) * Inject TARGET_JVM_VERSION_ATTRIBUTE for shadow jar * More checks in publishShadowJar * Use attributeProvider to track JavaPluginExtension changes * Update changelog * Cleanups * Check sourceCompatibility and simplify assertions --- src/docs/changes/README.md | 4 + .../gradle/plugins/shadow/PublishingTest.kt | 73 +++++++++++++++---- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 7 ++ 3 files changed, 69 insertions(+), 15 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 06abc4dd0..d5f4afc97 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Added** + +- Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) + **Changed** - Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index f2fad9d53..0b07b9800 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -60,14 +60,60 @@ class PublishingTest : BasePluginTest() { archiveClassifier = '' archiveBaseName = 'maven-all' """.trimIndent(), - ), + ) + System.lineSeparator(), ) publish() - assertShadowJarCommon(repoJarPath("shadow/maven-all/1.0/maven-all-1.0.jar")) - assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven-all/1.0/maven-all-1.0.module"))) + val assertions = { variantAttrs: Array>? -> + assertShadowJarCommon(repoJarPath("shadow/maven-all/1.0/maven-all-1.0.jar")) + assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) + val gmm = gmmAdapter.fromJson(repoPath("shadow/maven-all/1.0/maven-all-1.0.module")) + if (variantAttrs == null) { + assertShadowVariantCommon(gmm) + } else { + assertShadowVariantCommon(gmm, variantAttrs = variantAttrs) + } + } + + assertions(null) + + val attrsWithoutTargetJvm = shadowVariantAttrs.filterNot { (name, _) -> + name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name + }.toTypedArray() + val targetJvmAttr17 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "17" + val targetJvmAttr11 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "11" + + projectScriptPath.appendText( + """ + java { + toolchain.languageVersion = JavaLanguageVersion.of(17) + } + """.trimIndent() + System.lineSeparator(), + ) + publish() + assertions(attrsWithoutTargetJvm + targetJvmAttr17) + + projectScriptPath.appendText( + """ + java { + targetCompatibility = JavaVersion.VERSION_11 + } + """.trimIndent() + System.lineSeparator(), + ) + publish() + assertions(attrsWithoutTargetJvm + targetJvmAttr11) + + projectScriptPath.appendText( + """ + java { + sourceCompatibility = JavaVersion.VERSION_1_8 + } + """.trimIndent() + System.lineSeparator(), + ) + publish() + // sourceCompatibility doesn't affect the target JVM version. + assertions(attrsWithoutTargetJvm + targetJvmAttr11) } @Test @@ -265,10 +311,6 @@ class PublishingTest : BasePluginTest() { projectBlock = """ group = 'com.acme' version = '1.0' - java { - // This is necessary for `org.gradle.jvm.version` to be pinned on Java 8 for `components.java` MavenPublication. - targetCompatibility = JavaVersion.VERSION_1_8 - } """.trimIndent(), dependenciesBlock = """ implementation 'shadow:a:1.0' @@ -310,7 +352,6 @@ class PublishingTest : BasePluginTest() { *commonVariantAttrs, Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_API, - TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to JavaVersion.VERSION_1_8.majorVersion, ) transform { it.gavs }.isEmpty() } @@ -319,7 +360,6 @@ class PublishingTest : BasePluginTest() { *commonVariantAttrs, Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, - TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to JavaVersion.VERSION_1_8.majorVersion, ) transform { it.gavs }.containsOnly( "shadow:a:1.0", @@ -404,15 +444,12 @@ class PublishingTest : BasePluginTest() { private fun assertShadowVariantCommon( gmm: GradleModuleMetadata, + variantAttrs: Array> = shadowVariantAttrs, gavs: Array = arrayOf("shadow:b:1.0"), body: Assert.() -> Unit = {}, ) { assertThat(gmm.shadowRuntimeElementsVariant).all { - transform { it.attributes }.containsOnly( - *commonVariantAttrs, - Bundling.BUNDLING_ATTRIBUTE.name to Bundling.SHADOWED, - Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, - ) + transform { it.attributes }.containsOnly(*variantAttrs) transform { it.gavs }.containsOnly(*gavs) body() } @@ -439,6 +476,12 @@ class PublishingTest : BasePluginTest() { val commonVariantAttrs = arrayOf( Category.CATEGORY_ATTRIBUTE.name to Category.LIBRARY, LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name to LibraryElements.JAR, + TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to JavaVersion.current().majorVersion, + ) + + val shadowVariantAttrs = commonVariantAttrs + arrayOf( + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.SHADOWED, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, ) fun MavenXpp3Reader.read(path: Path): Model = path.inputStream().use { read(it) } 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 edcf9d3f3..93e313af0 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 @@ -10,9 +10,11 @@ import org.gradle.api.attributes.Bundling import org.gradle.api.attributes.Category import org.gradle.api.attributes.LibraryElements import org.gradle.api.attributes.Usage +import org.gradle.api.attributes.java.TargetJvmVersion import org.gradle.api.component.AdhocComponentWithVariants import org.gradle.api.component.SoftwareComponentFactory import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.tasks.SourceSetContainer import org.gradle.api.tasks.TaskProvider import org.gradle.jvm.tasks.Jar @@ -42,6 +44,11 @@ public abstract class ShadowJavaPlugin @Inject constructor( project.objects.named(LibraryElements::class.java, LibraryElements.JAR), ) attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling::class.java, Bundling.SHADOWED)) + val targetJvmVersion = project.provider { + project.extensions.getByType(JavaPluginExtension::class.java).targetCompatibility.majorVersion.toInt() + } + // Track JavaPluginExtension to update targetJvmVersion when it changes. + attr.attributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) } it.outgoing.artifact(shadowTaskProvider) } From 450b83108c8c058259919991111a88f00043a414 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 26 Jan 2025 06:55:33 -0500 Subject: [PATCH 192/941] Fix ShadowJar not being successful after includes or excludes are changed (#1200) * Update shadowJarIsCachedCorrectlyWhenUsingIncludesExcludes * Add internalIncludes and internalExcludes to delegate PatternFilterable methods * Revert "Add internalIncludes and internalExcludes to delegate PatternFilterable methods" This reverts commit 4d3f149e555162e0eb55cafa7973b3134f29e8d6. * Just mark includes and excludes as inputs * Update changelog * Test more times --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/docs/changes/README.md | 1 + .../shadow/caching/ShadowJarCachingTest.kt | 93 ++++++++++++------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 5 + 3 files changed, 66 insertions(+), 33 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index d5f4afc97..a06b7b552 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -14,6 +14,7 @@ **Fixed** - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) +- Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) ## [v9.0.0-beta6] (2025-01-23) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 3af4c28ed..e22cafd94 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import com.github.jengelman.gradle.plugins.shadow.util.isRegular @@ -56,73 +57,99 @@ class ShadowJarCachingTest : BaseCachingTest() { assertThat(jarPath("build/libs/foo-1.0-all.jar")).isRegular() } + @Issue( + "https://github.com/GradleUp/shadow/issues/717", + ) @Test fun shadowJarIsCachedCorrectlyWhenUsingIncludesExcludes() { + writeMainClass(className = "Main") + writeMainClass(className = "Main2") projectScriptPath.appendText( """ dependencies { - implementation 'junit:junit:3.8.2' - } - $shadowJar { - exclude 'junit/*' + implementation 'shadow:a:1.0' + implementation 'shadow:b:1.0' } - """.trimIndent(), + """.trimIndent() + System.lineSeparator(), ) - path("src/main/java/server/Server.java").writeText( - """ - package server; - import junit.framework.Test; - public class Server {} - """.trimIndent(), - ) - path("src/main/java/server/Util.java").writeText( + // First run successful with all files. + assertFirstExecutionSuccess() + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + "shadow/Main2.class", + "a.properties", + "a2.properties", + "b.properties", + ) + } + + projectScriptPath.appendText( """ - package server; - import junit.framework.Test; - public class Util {} - """.trimIndent(), + $shadowJar { + exclude '**.properties' + } + """.trimIndent() + System.lineSeparator(), ) - + // Second run successful after excludes changed. assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( - "server/Server.class", - "server/Util.class", + "shadow/Main.class", + "shadow/Main2.class", + ) + doesNotContainEntries( + "a.properties", + "a2.properties", + "b.properties", ) } - val replaced = projectScriptPath.readText().lines().dropLast(3).joinToString(System.lineSeparator()) - projectScriptPath.writeText( + projectScriptPath.appendText( """ - $replaced $shadowJar { - include 'server/*' - exclude '*/Util.*' + include 'shadow/Main.class' } - """.trimIndent(), + """.trimIndent() + System.lineSeparator(), ) + // Third run successful after includes changed. assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( - "server/Server.class", + "shadow/Main.class", ) doesNotContainEntries( - "server/Util.class", - "junit/framework/Test.class", + "shadow/Main2.class", + "a.properties", + "a2.properties", + "b.properties", ) } - assertExecutionsAreCachedAndUpToDate() + projectScriptPath.appendText( + """ + $shadowJar { + include 'shadow/Main2.class' + } + """.trimIndent() + System.lineSeparator(), + ) + // Forth run successful after includes changed again. + assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( - "server/Server.class", + "shadow/Main.class", + "shadow/Main2.class", ) doesNotContainEntries( - "server/Util.class", - "junit/framework/Test.class", + "a.properties", + "a2.properties", + "b.properties", ) } + + // Clean and run 2 more times to ensure the states are cached and up-to-date. + assertExecutionsAreCachedAndUpToDate() } @Test diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 0c00f4206..0b5672dbb 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -60,6 +60,11 @@ public abstract class ShadowJar : duplicatesStrategy = DuplicatesStrategy.INCLUDE manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) + // Ensure includes and excludes are considered as inputs. + // TODO: workaround for https://github.com/gradle/gradle/blob/236bdeb129e4d16c100667b677d4315448bc9ede/subprojects/core-api/src/main/java/org/gradle/api/tasks/util/PatternFilterable.java#L70-L84. + inputs.property("includes", includes) + inputs.property("excludes", excludes) + outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { // TODO: this has been called but the cache is still working fine, need to investigate why. transformers.get().any { !isCacheableTransform(it::class.java) } || From 343da8c14de0ec51278d480f7524c5627b4524cb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 26 Jan 2025 23:12:30 -0500 Subject: [PATCH 193/941] Address comments and stuffs (#1201) * Refine params and comments in DependencyFilter * Simplify AbstractDependencyFilter * Replace input marker TODOs * Override getIncludes and getExcludes explicitly for ShadowJar * Use getManifest to suppress the warning * Use MutableSet for Kotlin calls * Seems no need to indicate configuration to default * Cleanups --- api/shadow.api | 2 + .../internal/AbstractDependencyFilter.kt | 46 ++++--------------- .../shadow/internal/DependencyFilter.kt | 22 ++++----- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 13 +++--- .../transformers/ServiceFileTransformer.kt | 14 ++---- 5 files changed, 33 insertions(+), 64 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index ae77e4ad5..8b76a4145 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -307,7 +307,9 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getConfigurations ()Lorg/gradle/api/provider/SetProperty; public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; public fun getEnableRelocation ()Lorg/gradle/api/provider/Property; + public fun getExcludes ()Ljava/util/Set; public fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; + public fun getIncludes ()Ljava/util/Set; protected fun getInternalCompressor ()Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor; public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt index 58e8ed46b..db7f6ebe6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -23,11 +23,11 @@ internal sealed class AbstractDependencyFilter( ) override fun resolve(configuration: Configuration): FileCollection { - val includedDeps = mutableSetOf() - val excludedDeps = mutableSetOf() - resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, includedDeps, excludedDeps) + val included = mutableSetOf() + val excluded = mutableSetOf() + resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, included, excluded) return project.files(configuration.files) - - project.files(excludedDeps.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) + project.files(excluded.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) } override fun resolve(configurations: Collection): FileCollection { @@ -36,51 +36,26 @@ internal sealed class AbstractDependencyFilter( ?: project.files() } - /** - * Exclude dependencies that match the provided spec. - */ override fun exclude(spec: Spec): DependencyFilter = apply { excludeSpecs.add(spec) } - /** - * Include dependencies that match the provided spec. - */ override fun include(spec: Spec): DependencyFilter = apply { includeSpecs.add(spec) } - /** - * Create a spec that matches the provided project notation on group, name, and version. - */ override fun project(notation: Map): Spec { - return dependency(dependency = project.dependencies.project(notation)) + return dependency(project.dependencies.project(notation)) } - /** - * Create a spec that matches the default configuration for the provided project path on group, name, and version. - */ - override fun project(notation: String): Spec { - return dependency( - dependency = project.dependencies.project( - mapOf( - "path" to notation, - "configuration" to "default", - ), - ), - ) + override fun project(path: String): Spec { + return project(mapOf("path" to path)) } - /** - * Create a spec that matches dependencies using the provided notation on group, name, and version. - */ - override fun dependency(notation: Any): Spec { - return dependency(dependency = project.dependencies.create(notation)) + override fun dependency(dependencyNotation: Any): Spec { + return dependency(project.dependencies.create(dependencyNotation)) } - /** - * Create a spec that matches the provided dependency on group, name, and version. - */ override fun dependency(dependency: Dependency): Spec { return Spec { resolvedDependency -> (dependency.group == null || resolvedDependency.moduleGroup.matches(dependency.group!!.toRegex())) && @@ -89,9 +64,6 @@ internal sealed class AbstractDependencyFilter( } } - /** - * Create a spec that matches the provided closure. - */ override fun dependency(closure: Closure<*>): Spec { return Specs.convertClosureToSpec(closure) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt index 341e3377e..925f87f43 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt @@ -9,47 +9,47 @@ import org.gradle.api.specs.Spec public interface DependencyFilter { /** - * Resolve a [Configuration] against the include/exclude rules in the filter. + * Resolve a [configuration] against the [include]/[exclude] rules in the filter. */ public fun resolve(configuration: Configuration): FileCollection /** - * Resolve all [Configuration]s against the include/exclude rules in the filter and combine the results. + * Resolve all [configurations] against the [include]/[exclude] rules in the filter and combine the results. */ public fun resolve(configurations: Collection): FileCollection /** - * Exclude dependencies that match the provided spec. + * Exclude dependencies that match the provided [spec]. */ public fun exclude(spec: Spec): DependencyFilter /** - * Include dependencies that match the provided spec. + * Include dependencies that match the provided [spec]. */ public fun include(spec: Spec): DependencyFilter /** - * Create a spec that matches the provided project notation on group, name, and version. + * Create a [Spec] that matches the provided project [notation]. */ public fun project(notation: Map): Spec /** - * Create a spec that matches the default configuration for the provided project path on group, name, and version. + * Create a [Spec] that matches the provided project [path]. */ - public fun project(notation: String): Spec + public fun project(path: String): Spec /** - * Create a spec that matches dependencies using the provided notation on group, name, and version. + * Create a [Spec] that matches the provided [dependencyNotation]. */ - public fun dependency(notation: Any): Spec + public fun dependency(dependencyNotation: Any): Spec /** - * Create a spec that matches the provided dependency on group, name, and version. + * Create a [Spec] that matches the provided [dependency]. */ public fun dependency(dependency: Dependency): Spec /** - * Create a spec that matches the provided closure. + * Create a [Spec] that matches the provided [closure]. */ public fun dependency(closure: Closure<*>): Spec } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 0b5672dbb..5725a283f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -60,11 +60,6 @@ public abstract class ShadowJar : duplicatesStrategy = DuplicatesStrategy.INCLUDE manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) - // Ensure includes and excludes are considered as inputs. - // TODO: workaround for https://github.com/gradle/gradle/blob/236bdeb129e4d16c100667b677d4315448bc9ede/subprojects/core-api/src/main/java/org/gradle/api/tasks/util/PatternFilterable.java#L70-L84. - inputs.property("includes", includes) - inputs.property("excludes", excludes) - outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { // TODO: this has been called but the cache is still working fine, need to investigate why. transformers.get().any { !isCacheableTransform(it::class.java) } || @@ -160,7 +155,13 @@ public abstract class ShadowJar : public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) @Internal - override fun getManifest(): InheritManifest = super.manifest as InheritManifest + override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest + + @Input // TODO: https://github.com/GradleUp/shadow/issues/1202 + override fun getIncludes(): MutableSet = super.getIncludes() + + @Input // TODO: https://github.com/GradleUp/shadow/issues/1202 + override fun getExcludes(): MutableSet = super.getExcludes() override fun minimize(): ShadowJar = apply { minimizeJar.set(true) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 3f8023e64..d9476b6d6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -79,17 +79,11 @@ public open class ServiceFileTransformer( } } - /** - * {@inheritDoc} - */ - @Input - override fun getIncludes(): Set = patternSet.includes + @Input // TODO: https://github.com/GradleUp/shadow/issues/1202 + override fun getIncludes(): MutableSet = patternSet.includes - /** - * {@inheritDoc} - */ - @Input - override fun getExcludes(): Set = patternSet.excludes + @Input // TODO: https://github.com/GradleUp/shadow/issues/1202 + override fun getExcludes(): MutableSet = patternSet.excludes public open fun setPath(path: String): PatternFilterable = apply { patternSet.setIncludes(listOf("$path/**")) From 7bc393d22e076d5907b055bdf9d4357aede4ea1e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 27 Jan 2025 01:09:41 -0500 Subject: [PATCH 194/941] Avoid unnecessary for loops in PropertiesFileTransformer (#1204) --- .../plugins/shadow/transformers/PropertiesFileTransformer.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 27560fd88..f2dcd0222 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -174,12 +174,14 @@ public open class PropertiesFileTransformer @Inject constructor( } private fun transformKeys(properties: Properties): CleanProperties { + val keyTransformer = keyTransformer.get() + if (keyTransformer == IDENTITY) { return properties as CleanProperties } val result = CleanProperties() properties.forEach { (key, value) -> - result[keyTransformer.get().call(key as String)] = value + result[keyTransformer.call(key as String)] = value } return result } From db35be33fa1de48604ecb958bc6a0ebd26010e0b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 27 Jan 2025 17:23:18 +0800 Subject: [PATCH 195/941] Update plugin kotlin-jvm to v2.1.10 (#1205) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fd4963ba6..dcf70fd28 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ junit-bom = "org.junit:junit-bom:5.11.4" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] -kotlin-jvm = "org.jetbrains.kotlin.jvm:2.1.0" +kotlin-jvm = "org.jetbrains.kotlin.jvm:2.1.10" android-lint = "com.android.lint:8.8.0" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" spotless = "com.diffplug.spotless:7.0.2" From f08559a44797385fffa10079cb8f9828b0b715ed Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 27 Jan 2025 07:29:29 -0500 Subject: [PATCH 196/941] Get rid of Closure in public APIs (#1203) * Replace Closure with Spec in DependencyFilter * Fix excludeATransitiveProjectDependency * Remove `DependencyFilter#dependency(Spec)`, use `exclude` or `include` directly * Try to replace Closure with JavaFunction in PropertiesFileTransformer * Rplace Property with Function for keyTransformer --- api/shadow.api | 4 ++-- .../jengelman/gradle/plugins/shadow/FilteringTest.kt | 2 +- .../plugins/shadow/internal/AbstractDependencyFilter.kt | 6 ------ .../gradle/plugins/shadow/internal/DependencyFilter.kt | 6 ------ .../shadow/transformers/PropertiesFileTransformer.kt | 9 +++------ .../shadow/transformers/PropertiesFileTransformerTest.kt | 7 +------ 6 files changed, 7 insertions(+), 27 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 8b76a4145..719f47544 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -92,7 +92,6 @@ public class com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper : } public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter { - public abstract fun dependency (Lgroovy/lang/Closure;)Lorg/gradle/api/specs/Spec; public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; public abstract fun exclude (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter; @@ -519,7 +518,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesF public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getCharsetName ()Lorg/gradle/api/provider/Property; - public fun getKeyTransformer ()Lorg/gradle/api/provider/Property; + public fun getKeyTransformer ()Lkotlin/jvm/functions/Function1; public fun getMappings ()Lorg/gradle/api/provider/MapProperty; public fun getMergeSeparator ()Lorg/gradle/api/provider/Property; public fun getMergeStrategy ()Lorg/gradle/api/provider/Property; @@ -527,6 +526,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesF public fun getPaths ()Lorg/gradle/api/provider/SetProperty; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun setKeyTransformer (Lkotlin/jvm/functions/Function1;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index fd7f0fe64..6f89bda6b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -215,7 +215,7 @@ class FilteringTest : BasePluginTest() { writeClientAndServerModules( serverShadowBlock = """ dependencies { - exclude(dependency { it.moduleGroup == 'junit' }) + exclude { it.moduleGroup == 'junit' } } """.trimIndent(), ) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt index db7f6ebe6..cf4c946db 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.internal -import groovy.lang.Closure import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency @@ -8,7 +7,6 @@ import org.gradle.api.artifacts.ResolvedArtifact import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.file.FileCollection import org.gradle.api.specs.Spec -import org.gradle.api.specs.Specs internal sealed class AbstractDependencyFilter( @Transient private val project: Project, @@ -64,10 +62,6 @@ internal sealed class AbstractDependencyFilter( } } - override fun dependency(closure: Closure<*>): Spec { - return Specs.convertClosureToSpec(closure) - } - protected fun ResolvedDependency.isIncluded(): Boolean { val include = includeSpecs.isEmpty() || includeSpecs.any { it.isSatisfiedBy(this) } val exclude = excludeSpecs.isNotEmpty() && excludeSpecs.any { it.isSatisfiedBy(this) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt index 925f87f43..9acc41f23 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.internal -import groovy.lang.Closure import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ResolvedDependency @@ -47,9 +46,4 @@ public interface DependencyFilter { * Create a [Spec] that matches the provided [dependency]. */ public fun dependency(dependency: Dependency): Spec - - /** - * Create a [Spec] that matches the provided [closure]. - */ - public fun dependency(closure: Closure<*>): Spec } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index f2dcd0222..258c63368 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -6,8 +6,6 @@ import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy -import groovy.lang.Closure -import groovy.lang.Closure.IDENTITY import java.io.InputStream import java.nio.charset.Charset import java.util.Properties @@ -124,7 +122,7 @@ public open class PropertiesFileTransformer @Inject constructor( public open val charsetName: Property = objectFactory.property(Charsets.ISO_8859_1.name()) @get:Internal - public open val keyTransformer: Property> = objectFactory.property(IDENTITY) + public open var keyTransformer: (String) -> String = IDENTITY override fun canTransformResource(element: FileTreeElement): Boolean { val mappings = mappings.get() @@ -174,14 +172,12 @@ public open class PropertiesFileTransformer @Inject constructor( } private fun transformKeys(properties: Properties): CleanProperties { - val keyTransformer = keyTransformer.get() - if (keyTransformer == IDENTITY) { return properties as CleanProperties } val result = CleanProperties() properties.forEach { (key, value) -> - result[keyTransformer.call(key as String)] = value + result[keyTransformer(key as String)] = value } return result } @@ -253,5 +249,6 @@ public open class PropertiesFileTransformer @Inject constructor( private companion object { private const val PROPERTIES_SUFFIX = ".properties" + private val IDENTITY = { key: String -> key } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index 33a16ff62..73de42b6e 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -8,7 +8,6 @@ import assertk.assertions.isNotEmpty import assertk.assertions.isTrue import com.github.jengelman.gradle.plugins.shadow.internal.inputStream import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy -import groovy.lang.Closure import java.nio.charset.Charset import java.util.Properties import org.junit.jupiter.api.Test @@ -132,11 +131,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest, ) { transformer.mergeStrategy.set(MergeStrategy.Append) - transformer.keyTransformer.set(object : Closure(null) { - override fun call(vararg arguments: Any?): String { - return keyTransformer.invoke(arguments.first() as String) - } - }) + transformer.keyTransformer = keyTransformer if (transformer.canTransformResource(path)) { transformer.transform(context(path, input1)) From ed7135b1a29b615702ba2666a135d57f6fc4afc6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 27 Jan 2025 20:46:08 -0500 Subject: [PATCH 197/941] Refine parameterized unit tests (#1207) * Remove extra ParameterizedTest names They are converted from the original Spock tests, we don't need them. * Unify MethodSource names --- .../shadow/transformers/TransformersTest.kt | 4 +- .../Log4j2PluginsCacheFileTransformerTest.kt | 9 +++-- .../PropertiesFileTransformerTest.kt | 40 +++++++++---------- .../ServiceFileTransformerTest.kt | 12 +++--- 4 files changed, 33 insertions(+), 32 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 48bf68dbd..e66ca1340 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -121,7 +121,7 @@ class TransformersTest : BaseTransformerTest() { } @ParameterizedTest - @MethodSource("transformerConfigurations") + @MethodSource("transformerConfigProvider") fun otherTransformers(pair: Pair>) { val (configuration, transformer) = pair if (configuration.contains("test/some.file")) { @@ -169,7 +169,7 @@ class TransformersTest : BaseTransformerTest() { """.trimIndent() @JvmStatic - fun transformerConfigurations() = listOf( + fun transformerConfigProvider() = listOf( "" to ApacheLicenseResourceTransformer::class, "" to ApacheNoticeResourceTransformer::class, "" to ComponentsXmlResourceTransformer::class, diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index 19c082d4c..c37464df7 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -6,6 +6,7 @@ import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isNotEqualTo import assertk.assertions.isTrue +import assertk.assertions.startsWith import assertk.fail import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator @@ -92,8 +93,8 @@ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest, @@ -101,8 +101,8 @@ class PropertiesFileTransformerTest : BaseTransformerTest>, @@ -121,8 +121,8 @@ class PropertiesFileTransformerTest : BaseTransformerTest String, @@ -141,8 +141,8 @@ class PropertiesFileTransformerTest : BaseTransformerTest key }, diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 82c0eadf6..c2841d399 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -10,8 +10,8 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource class ServiceFileTransformerTest : BaseTransformerTest() { - @ParameterizedTest(name = "{index} => path={0}, exclude={1}, expected={2}") - @MethodSource("canTransformResourceData") + @ParameterizedTest + @MethodSource("resourceProvider") fun canTransformResource(path: String, exclude: Boolean, expected: Boolean) { if (exclude) { transformer.exclude(path) @@ -19,8 +19,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() assertThat(transformer.canTransformResource(path)).isEqualTo(expected) } - @ParameterizedTest(name = "{index} => path={0}") - @MethodSource("transformsServiceFileData") + @ParameterizedTest + @MethodSource("serviceFileProvider") fun transformServiceFile(path: String, input1: String, input2: String, output: String) { if (transformer.canTransformResource(path)) { transformer.transform(context(path, input1)) @@ -44,7 +44,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() } @JvmStatic - fun canTransformResourceData() = listOf( + fun resourceProvider() = listOf( // path, exclude, expected Arguments.of("META-INF/services/java.sql.Driver", false, true), Arguments.of("META-INF/services/io.dropwizard.logging.AppenderFactory", false, true), @@ -55,7 +55,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() ) @JvmStatic - fun transformsServiceFileData() = listOf( + fun serviceFileProvider() = listOf( // path, input1, input2, output Arguments.of("META-INF/services/com.acme.Foo", "foo", "bar", "foo\nbar"), Arguments.of("META-INF/services/com.acme.Bar", "foo\nbar", "zoo", "foo\nbar\nzoo"), From b6042aefbee526028cbf9b85deef0ab4b5adcf0e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 27 Jan 2025 22:05:27 -0500 Subject: [PATCH 198/941] Polish transformer caching tests (#1209) * Use Path.deleteExisting * Reuse assertions in shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer * Reuse assertions in shadowJarIsCachedCorrectlyWhenUsingAppendingTransformer * Reuse assertions in shadowJarIsCachedCorrectlyWhenUsingXmlAppendingTransformer * Reuse assertions in shadowJarIsCachedCorrectlyWhenUsingGroovyExtensionModuleTransformer * Rename TransformCachingTest to TransformerCachingTest * Reuse writeMainClass --- ...chingTest.kt => TransformerCachingTest.kt} | 119 ++++++++---------- 1 file changed, 54 insertions(+), 65 deletions(-) rename src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/{TransformCachingTest.kt => TransformerCachingTest.kt} (60%) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt similarity index 60% rename from src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 63257e886..4391bf1f8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -1,25 +1,38 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText +import kotlin.io.path.deleteExisting import kotlin.io.path.readText import kotlin.io.path.writeText +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -class TransformCachingTest : BaseCachingTest() { +class TransformerCachingTest : BaseCachingTest() { + @BeforeEach + override fun setup() { + super.setup() + writeMainClass() + } + @Test fun shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer() { - writeMainClass() + val assertions = { + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + } assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions() projectScriptPath.appendText( transform( @@ -28,40 +41,34 @@ class TransformCachingTest : BaseCachingTest() { """.trimIndent(), ), ) - assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions() assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions() val replaced = projectScriptPath.readText().replace("META-INF/foo", "META-INF/bar") projectScriptPath.writeText(replaced) assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions() assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions() } @Test fun shadowJarIsCachedCorrectlyWhenUsingAppendingTransformer() { path("src/main/resources/foo/bar.properties").writeText("foo=bar") - writeMainClass() + val assertions = { name: String -> + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/$name.properties") + getContent("foo/$name.properties").isEqualTo("foo=$name") + } + } assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions("bar") projectScriptPath.appendText( transform( @@ -70,42 +77,36 @@ class TransformCachingTest : BaseCachingTest() { """.trimIndent(), ), ) - assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class", "foo/bar.properties") - } + assertions("bar") assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class", "foo/bar.properties") - } + assertions("bar") - path("src/main/resources/foo/bar.properties").toFile().delete() + path("src/main/resources/foo/bar.properties").deleteExisting() path("src/main/resources/foo/baz.properties").writeText("foo=baz") val replaced = projectScriptPath.readText().replace("foo/bar.properties", "foo/baz.properties") projectScriptPath.writeText(replaced) assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class", "foo/baz.properties") - } + assertions("baz") assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class", "foo/baz.properties") - } + assertions("baz") } @Test fun shadowJarIsCachedCorrectlyWhenUsingXmlAppendingTransformer() { path("src/main/resources/foo/bar.xml").writeText("bar") - writeMainClass() + val assertions = { name: String -> + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class", "foo/$name.xml") + getContent("foo/$name.xml").contains("$name") + } + } assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions("bar") projectScriptPath.appendText( transform( @@ -114,54 +115,42 @@ class TransformCachingTest : BaseCachingTest() { """.trimIndent(), ), ) - assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class", "foo/bar.xml") - } + assertions("bar") assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class", "foo/bar.xml") - } + assertions("bar") - path("src/main/resources/foo/bar.xml").toFile().delete() + path("src/main/resources/foo/bar.xml").deleteExisting() path("src/main/resources/foo/baz.xml").writeText("baz") val replaced = projectScriptPath.readText().replace("foo/bar.xml", "foo/baz.xml") projectScriptPath.writeText(replaced) assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class", "foo/baz.xml") - } + assertions("baz") assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class", "foo/baz.xml") - } + assertions("baz") } @Test fun shadowJarIsCachedCorrectlyWhenUsingGroovyExtensionModuleTransformer() { - writeMainClass() + val assertions = { + assertThat(outputShadowJar).useAll { + containsEntries("shadow/Main.class") + } + } assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions() projectScriptPath.appendText( transform(), ) - assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions() assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries("shadow/Main.class") - } + assertions() } } From 0359eb94c0bf7ce23ce9d0788f7cb4485b6d8980 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 27 Jan 2025 22:29:39 -0500 Subject: [PATCH 199/941] More cacheable Transformers (#1210) * Add shadowJarIsCachedCorrectlyWhenUsingOtherTransformers * Mark more transformers cacheable * Update changelog --- src/docs/changes/README.md | 1 + .../shadow/caching/TransformerCachingTest.kt | 43 +++++++++++++++++-- .../ApacheLicenseResourceTransformer.kt | 1 + .../ApacheNoticeResourceTransformer.kt | 1 + .../ComponentsXmlResourceTransformer.kt | 1 + .../DontIncludeResourceTransformer.kt | 1 + .../IncludeResourceTransformer.kt | 1 + .../ManifestAppenderTransformer.kt | 1 + .../ManifestResourceTransformer.kt | 1 + .../transformers/PropertiesFileTransformer.kt | 1 + 10 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index a06b7b552..db14f59fb 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -10,6 +10,7 @@ **Changed** - Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) +- Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) **Fixed** diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 4391bf1f8..4e0365ea5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -3,8 +3,17 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.transformers.ApacheLicenseResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ApacheNoticeResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ComponentsXmlResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.DontIncludeResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.IncludeResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestAppenderTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer import com.github.jengelman.gradle.plugins.shadow.util.containsEntries @@ -13,8 +22,11 @@ import kotlin.io.path.appendText import kotlin.io.path.deleteExisting import kotlin.io.path.readText import kotlin.io.path.writeText +import kotlin.reflect.KClass import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource class TransformerCachingTest : BaseCachingTest() { @BeforeEach @@ -133,8 +145,13 @@ class TransformerCachingTest : BaseCachingTest() { assertions("baz") } - @Test - fun shadowJarIsCachedCorrectlyWhenUsingGroovyExtensionModuleTransformer() { + @ParameterizedTest + @MethodSource("transformerConfigProvider") + fun shadowJarIsCachedCorrectlyWhenUsingOtherTransformers(pair: Pair>) { + val (configuration, transformer) = pair + if (configuration.contains("test/some.file")) { + path("test/some.file").writeText("some content") + } val assertions = { assertThat(outputShadowJar).useAll { containsEntries("shadow/Main.class") @@ -145,7 +162,11 @@ class TransformerCachingTest : BaseCachingTest() { assertions() projectScriptPath.appendText( - transform(), + """ + $shadowJar { + transform(${transformer.java.name}) $configuration + } + """.trimIndent(), ) assertFirstExecutionSuccess() assertions() @@ -153,4 +174,20 @@ class TransformerCachingTest : BaseCachingTest() { assertExecutionsAreCachedAndUpToDate() assertions() } + + private companion object { + @JvmStatic + fun transformerConfigProvider() = listOf( + "" to ApacheLicenseResourceTransformer::class, + "" to ApacheNoticeResourceTransformer::class, + "" to ComponentsXmlResourceTransformer::class, + "" to DontIncludeResourceTransformer::class, + "" to GroovyExtensionModuleTransformer::class, + "{ resource.set(\"test.file\"); file.fileValue(file(\"test/some.file\")) }" to IncludeResourceTransformer::class, + "" to Log4j2PluginsCacheFileTransformer::class, + "" to ManifestAppenderTransformer::class, + "" to ManifestResourceTransformer::class, + "{ keyTransformer = { it.toLowerCase() } }" to PropertiesFileTransformer::class, + ) + } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index 67d4155ad..71a5e718d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -9,6 +9,7 @@ import org.gradle.api.file.FileTreeElement * * @author John Engelman */ +@CacheableTransformer public open class ApacheLicenseResourceTransformer : Transformer by NoOpTransformer { override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.relativePath.pathString diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 5c03b90d4..5fa6bf7b9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -23,6 +23,7 @@ import org.gradle.api.tasks.Optional * * @author John Engelman */ +@CacheableTransformer public open class ApacheNoticeResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : Transformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index 8c273e6e7..c82431639 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -22,6 +22,7 @@ import org.gradle.api.tasks.Internal * * @author John Engelman */ +@CacheableTransformer public open class ComponentsXmlResourceTransformer : Transformer { private val components = mutableMapOf() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index f28ad6e7e..c88badee5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -15,6 +15,7 @@ import org.gradle.api.tasks.Optional * * @author John Engelman */ +@CacheableTransformer public open class DontIncludeResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : Transformer by NoOpTransformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index 5ca9bb1ec..bd0646161 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -20,6 +20,7 @@ import org.gradle.api.tasks.PathSensitivity * * @author John Engelman */ +@CacheableTransformer public open class IncludeResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : Transformer by NoOpTransformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index f8bf89614..8617b6847 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -21,6 +21,7 @@ import org.slf4j.LoggerFactory * Modified from [ManifestResourceTransformer]. * @author Chris Rankin */ +@CacheableTransformer public open class ManifestAppenderTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : Transformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 1de0158ef..5c9c8dd75 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -28,6 +28,7 @@ import org.slf4j.LoggerFactory * @author Jason van Zyl * @author John Engelman */ +@CacheableTransformer public open class ManifestResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : Transformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 258c63368..06d0d3450 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -98,6 +98,7 @@ import org.gradle.api.tasks.Internal * @author Andres Almiray * @author Marc Philipp */ +@CacheableTransformer public open class PropertiesFileTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : Transformer { From 83ca343828f124f4b4bd6740f83c4f4f140ef83d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 28 Jan 2025 05:01:46 -0500 Subject: [PATCH 200/941] Mark ShadowJar.dependencyFilter as Input (#1206) * Mark dependencyFilter as Input * Mark stats as intenral modifier * Mark DependencyFilter as Serializable * Mark Transient, Optional, and Input for keyTransformer * Revert "Mark Transient, Optional, and Input for keyTransformer" This reverts commit 58564d37edff1ba1b913d3ba6bc4557d828b96b0. * Comment it * Update changelog --- api/shadow.api | 4 +--- src/docs/changes/README.md | 2 ++ .../gradle/plugins/shadow/internal/DependencyFilter.kt | 4 +++- .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 8 ++++---- .../jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt | 3 --- .../shadow/transformers/PropertiesFileTransformer.kt | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 719f47544..baf6d96d3 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -91,7 +91,7 @@ public class com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper : public fun mapValue (Ljava/lang/Object;)Ljava/lang/Object; } -public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter { +public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter : java/io/Serializable { public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; public abstract fun exclude (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter; @@ -317,7 +317,6 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getRelocators ()Lorg/gradle/api/provider/SetProperty; protected fun getRootPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; public fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/ConfigurableFileCollection; - public fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; public fun getToMinimize ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getTransformers ()Lorg/gradle/api/provider/SetProperty; public fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; @@ -354,7 +353,6 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun append (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; public abstract fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun mergeServiceFiles (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index db14f59fb..8855acfc6 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -11,6 +11,8 @@ - Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) - Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) +- Mark `ShadowJar.dependencyFilter` as `@Input`. ([#1206](https://github.com/GradleUp/shadow/pull/1206)) + `ShadowSpec.stats` is removed and `ShadowJar.stats` is `internal` for now. **Fixed** diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt index 9acc41f23..213c831a2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt @@ -1,12 +1,14 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import java.io.Serializable import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.file.FileCollection import org.gradle.api.specs.Spec -public interface DependencyFilter { +// DependencyFilter is used as Gradle Input in ShadowJar, so it must be Serializable. +public interface DependencyFilter : Serializable { /** * Resolve a [configuration] against the [include]/[exclude] rules in the filter. */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 5725a283f..214c99ac9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -67,9 +67,6 @@ public abstract class ShadowJar : } } - @get:Internal - override val stats: ShadowStats = ShadowStats() - /** * Minimize the jar by removing unused classes. * @@ -105,6 +102,9 @@ public abstract class ShadowJar : } } + @get:Internal + internal val stats: ShadowStats = ShadowStats() + @get:Internal protected open val rootPatternSet: PatternSet get() = (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet @@ -129,7 +129,7 @@ public abstract class ShadowJar : @get:Optional public open val configurations: SetProperty = objectFactory.setProperty() - @get:Internal + @get:Input public open val dependencyFilter: Property = objectFactory.property(DefaultDependencyFilter(project)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt index 8f360f642..b652bde3d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.tasks -import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator @@ -68,6 +67,4 @@ public interface ShadowSpec : CopySpec { InvocationTargetException::class, ) public fun relocate(clazz: Class, action: Action?): ShadowSpec - - public val stats: ShadowStats } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 06d0d3450..4ea1b9fed 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -122,7 +122,7 @@ public open class PropertiesFileTransformer @Inject constructor( @get:Input public open val charsetName: Property = objectFactory.property(Charsets.ISO_8859_1.name()) - @get:Internal + @get:Internal // TODO: should be @Input, but it can't be serialized, see https://github.com/GradleUp/shadow/pull/1208. public open var keyTransformer: (String) -> String = IDENTITY override fun canTransformResource(element: FileTreeElement): Boolean { From 529edc1fe366d268bf707d34a10b1c5e8c69e2f2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 28 Jan 2025 05:18:38 -0500 Subject: [PATCH 201/941] Shorten caching tests (#1211) * Tweak MinimizationCachingTest * Tweak RelocationCachingTest * Tweak ShadowJarCachingTest --- .../shadow/caching/MinimizationCachingTest.kt | 32 ++++++--------- .../shadow/caching/RelocationCachingTest.kt | 32 ++++++--------- .../shadow/caching/ShadowJarCachingTest.kt | 41 +++++++++---------- 3 files changed, 46 insertions(+), 59 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt index 3921a87a1..98e73f91a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt @@ -40,27 +40,21 @@ class MinimizationCachingTest : BaseCachingTest() { } """.trimIndent(), ) - - assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries( - "server/Server.class", - "junit/framework/Test.class", - ) - doesNotContainEntries( - "client/Client.class", - ) + val assertions = { + assertThat(outputShadowJar).useAll { + containsEntries( + "server/Server.class", + "junit/framework/Test.class", + ) + doesNotContainEntries( + "client/Client.class", + ) + } } + assertFirstExecutionSuccess() + assertions() assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries( - "server/Server.class", - "junit/framework/Test.class", - ) - doesNotContainEntries( - "client/Client.class", - ) - } + assertions() } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index 43a05f563..eda6b5189 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -36,27 +36,21 @@ class RelocationCachingTest : BaseCachingTest() { } """.trimIndent(), ) - - assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries( - "shadow/Main.class", - "foo/junit/framework/Test.class", - ) - doesNotContainEntries( - "junit/framework/Test.class", - ) + val assertions = { + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + "foo/junit/framework/Test.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } } + assertFirstExecutionSuccess() + assertions() assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries( - "shadow/Main.class", - "foo/junit/framework/Test.class", - ) - doesNotContainEntries( - "junit/framework/Test.class", - ) - } + assertions() } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index e22cafd94..c61248abc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -27,11 +27,13 @@ class ShadowJarCachingTest : BaseCachingTest() { assertFirstExecutionSuccess() assertExecutionsAreCachedAndUpToDate() - val replaced = projectScriptPath.readText().lines().filter { - it != fromJar(projectJar) - }.joinToString(System.lineSeparator()) + val replaced = projectScriptPath.readText().lines() + .filterNot { it == fromJar(projectJar) } + .joinToString(System.lineSeparator()) projectScriptPath.writeText(replaced) + assertFirstExecutionSuccess() + assertExecutionsAreCachedAndUpToDate() } @Test @@ -45,6 +47,7 @@ class ShadowJarCachingTest : BaseCachingTest() { ) assertFirstExecutionSuccess() + assertThat(outputShadowJar).isRegular() projectScriptPath.appendText( """ @@ -53,6 +56,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } """.trimIndent(), ) + assertExecutionsAreCachedAndUpToDate() assertThat(jarPath("build/libs/foo-1.0-all.jar")).isRegular() } @@ -154,6 +158,7 @@ class ShadowJarCachingTest : BaseCachingTest() { @Test fun shadowJarIsCachedCorrectlyWhenUsingDependencyIncludesExcludes() { + writeMainClass(withImports = true) projectScriptPath.appendText( """ dependencies { @@ -162,8 +167,6 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) - writeMainClass(withImports = true) - assertFirstExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( @@ -181,24 +184,20 @@ class ShadowJarCachingTest : BaseCachingTest() { } """.trimIndent(), ) - assertFirstExecutionSuccess() - assertThat(outputShadowJar).useAll { - containsEntries( - "shadow/Main.class", - ) - doesNotContainEntries( - "junit/framework/Test.class", - ) + val assertions = { + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } } + assertFirstExecutionSuccess() + assertions() assertExecutionsAreCachedAndUpToDate() - assertThat(outputShadowJar).useAll { - containsEntries( - "shadow/Main.class", - ) - doesNotContainEntries( - "junit/framework/Test.class", - ) - } + assertions() } } From 3c08e5aa10f839b2b25245df4b8e2e3fd54025c6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 28 Jan 2025 09:39:24 -0500 Subject: [PATCH 202/941] Add a caching test for ShadowJar.dependencyFilter (#1212) * Reuse publishArtifactCD * Add shadowJarIsCachedCorrectlyAfterDependencyFilterChanged I missed that we can use `MinimizeDependencyFilter` as the fallback for testing. --- .../gradle/plugins/shadow/BasePluginTest.kt | 16 ++++++++ .../gradle/plugins/shadow/FilteringTest.kt | 16 -------- .../shadow/caching/ShadowJarCachingTest.kt | 38 +++++++++++++++++++ 3 files changed, 54 insertions(+), 16 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 536277fcb..f1755ed5e 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 @@ -158,6 +158,22 @@ abstract class BasePluginTest { return runnerBlock(runner(tasks.toList())).buildAndFail().assertNoDeprecationWarnings() } + fun publishArtifactCD(circular: Boolean = false) { + localRepo.module("shadow", "c", "1.0") { + buildJar { + insert("c.properties", "c") + } + if (circular) { + addDependency("shadow", "d", "1.0") + } + }.module("shadow", "d", "1.0") { + buildJar { + insert("d.properties", "d") + } + addDependency("shadow", "c", "1.0") + }.publish() + } + fun writeMainClass( sourceSet: String = "main", withImports: Boolean = false, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 6f89bda6b..319d341f4 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -296,20 +296,4 @@ class FilteringTest : BasePluginTest() { ) } } - - private fun publishArtifactCD(circular: Boolean = false) { - localRepo.module("shadow", "c", "1.0") { - buildJar { - insert("c.properties", "c") - } - if (circular) { - addDependency("shadow", "d", "1.0") - } - }.module("shadow", "d", "1.0") { - buildJar { - insert("d.properties", "d") - } - addDependency("shadow", "c", "1.0") - }.publish() - } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index c61248abc..97f5d5c72 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -200,4 +200,42 @@ class ShadowJarCachingTest : BaseCachingTest() { assertExecutionsAreCachedAndUpToDate() assertions() } + + @Test + fun shadowJarIsCachedCorrectlyAfterDependencyFilterChanged() { + publishArtifactCD() + projectScriptPath.appendText( + """ + dependencies { + implementation 'shadow:d:1.0' + } + """.trimIndent() + System.lineSeparator(), + ) + val assertions = { + assertThat(outputShadowJar).useAll { + containsEntries( + "c.properties", + "d.properties", + ) + } + } + + assertFirstExecutionSuccess() + assertions() + assertExecutionsAreCachedAndUpToDate() + assertions() + + projectScriptPath.appendText( + """ + $shadowJar { + dependencyFilter = new com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter(project) + } + """.trimIndent(), + ) + + assertFirstExecutionSuccess() + assertions() + assertExecutionsAreCachedAndUpToDate() + assertions() + } } From 15f96762dd220571b7305b77699df8232e31492d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 28 Jan 2025 10:03:59 -0500 Subject: [PATCH 203/941] Remove unused XSI_NS const value (#1213) --- api/shadow.api | 5 ----- .../plugins/shadow/transformers/XmlAppendingTransformer.kt | 4 ---- 2 files changed, 9 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index baf6d96d3..a87057859 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -630,8 +630,6 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans } public class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer$Companion; - public static final field XSI_NS Ljava/lang/String; public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getIgnoreDtd ()Lorg/gradle/api/provider/Property; @@ -642,6 +640,3 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendin public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public final class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer$Companion { -} - diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index da28716b6..ed0b8d52d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -81,8 +81,4 @@ public open class XmlAppendingTransformer @Inject constructor( XMLOutputter(Format.getPrettyFormat()).output(doc, os) doc = null } - - public companion object { - public const val XSI_NS: String = "http://www.w3.org/2001/XMLSchema-instance" - } } From 7a7b4a0299bb050aaea390983e99487b5528308d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 28 Jan 2025 10:34:06 -0500 Subject: [PATCH 204/941] Be friend with functionalTest (#1172) * Workaround for https://youtrack.jetbrains.com/issue/KTIJ-7662 * Place `kotlin.target.compilations` after `testing.suites` * Reuse `requireResourceAsPath` * Ref `MinimizeDependencyFilter` class name directly * Remove `implementation sourceSets.main.get().output` --- build.gradle.kts | 11 +++++++++-- .../jengelman/gradle/plugins/shadow/BasePluginTest.kt | 9 +-------- .../plugins/shadow/caching/ShadowJarCachingTest.kt | 3 ++- .../plugins/shadow/transformers/TransformersTest.kt | 4 ++-- .../jengelman/gradle/plugins/shadow/internal/Utils.kt | 9 +++++++++ 5 files changed, 23 insertions(+), 13 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 362ced927..0feaab7ba 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -105,8 +105,6 @@ testing.suites { } } dependencies { - // Seems we can't ref project() here due to some limitations of rootProject. - implementation(sourceSets.main.get().output) implementation(libs.apache.maven.modelBuilder) implementation(libs.moshi) implementation(libs.moshi.kotlin) @@ -127,6 +125,15 @@ testing.suites { } } +// This part should be placed after testing.suites to ensure the test sourceSets are created. +kotlin.target.compilations { + val main by getting + val functionalTest by getting { + // TODO: https://youtrack.jetbrains.com/issue/KTIJ-7662 + associateWith(main) + } +} + gradlePlugin { testSourceSets( sourceSets["functionalTest"], 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 f1755ed5e..3f4aee9db 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 @@ -7,12 +7,12 @@ import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import assertk.assertions.isNotNull import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarPath import java.io.Closeable -import java.nio.file.NoSuchFileException import java.nio.file.Path import java.util.Properties import kotlin.io.path.ExperimentalPathApi @@ -26,7 +26,6 @@ import kotlin.io.path.createTempDirectory import kotlin.io.path.deleteRecursively import kotlin.io.path.exists import kotlin.io.path.readText -import kotlin.io.path.toPath import kotlin.io.path.writeText import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner @@ -382,11 +381,5 @@ abstract class BasePluginTest { fun Assert.taskOutcomeEquals(taskPath: String, expectedOutcome: TaskOutcome) { return transform { it.task(taskPath)?.outcome }.isNotNull().isEqualTo(expectedOutcome) } - - fun requireResourceAsPath(name: String): Path { - val resource = this::class.java.classLoader.getResource(name) - ?: throw NoSuchFileException("Resource $name not found.") - return resource.toURI().toPath() - } } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 97f5d5c72..4d8f569a6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries @@ -228,7 +229,7 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - dependencyFilter = new com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter(project) + dependencyFilter = new ${MinimizeDependencyFilter::class.java.name}(project) } """.trimIndent(), ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index e66ca1340..2f56d0183 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -6,12 +6,12 @@ import assertk.assertions.isEqualTo import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull import assertk.assertions.isNull +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.getStream import com.github.jengelman.gradle.plugins.shadow.util.isRegular import java.util.jar.Attributes import kotlin.io.path.appendText -import kotlin.io.path.readText import kotlin.io.path.writeText import kotlin.reflect.KClass import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE @@ -77,7 +77,7 @@ class TransformersTest : BaseTransformerTest() { ) @Test fun canMergeLog4j2PluginCacheFiles() { - val content = requireResourceAsPath(PLUGIN_CACHE_FILE).readText() + val content = requireResourceAsText(PLUGIN_CACHE_FILE) val one = buildJarOne { insert(PLUGIN_CACHE_FILE, content) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 97557c31c..b98ef3d6a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -6,7 +6,10 @@ import java.io.File import java.io.InputStream import java.nio.charset.Charset import java.nio.file.NoSuchFileException +import java.nio.file.Path import java.util.Properties +import kotlin.io.path.inputStream +import kotlin.io.path.toPath import org.gradle.api.file.RelativePath import org.gradle.api.internal.file.DefaultFileTreeElement import org.gradle.internal.file.Chmod @@ -46,6 +49,12 @@ internal fun requireResourceAsStream(name: String): InputStream { ?: throw NoSuchFileException("Resource $name not found.") } +internal fun requireResourceAsPath(name: String): Path { + val resource = Utils::class.java.classLoader.getResource(name) + ?: throw NoSuchFileException("Resource $name not found.") + return resource.toURI().toPath() +} + private val DummyFile = File("dummy") private val DummyChmod = Chmod { _, _ -> error("This is a dummy implementation.") } private val DummyStat = object : Stat { From d95c378b6f322b0310be90357c75b28605c214cd Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 28 Jan 2025 20:43:33 -0500 Subject: [PATCH 205/941] Tweak caching test base (#1215) --- .../plugins/shadow/ApplicationPluginTest.kt | 7 ++-- .../gradle/plugins/shadow/BasePluginTest.kt | 6 +++- .../plugins/shadow/caching/BaseCachingTest.kt | 26 ++++++++------ .../shadow/caching/MinimizationCachingTest.kt | 8 ++--- .../shadow/caching/RelocationCachingTest.kt | 6 ++-- .../shadow/caching/ShadowJarCachingTest.kt | 36 +++++++++---------- .../shadow/caching/TransformerCachingTest.kt | 36 +++++++++---------- 7 files changed, 65 insertions(+), 60 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index cc9a0e0db..27a1f636a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -5,7 +5,6 @@ import assertk.assertions.contains import assertk.assertions.containsOnly import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME -import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.DISTRIBUTION_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath @@ -55,7 +54,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - val result = run(SHADOW_RUN_TASK_NAME) + val result = run(runShadowTask) assertThat(result.output).contains( "Running application with JDK 17", @@ -191,7 +190,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - val result = run(SHADOW_RUN_TASK_NAME) + val result = run(runShadowTask) assertThat(result.output).contains( "Hello, World! (foo) from Main2", @@ -279,8 +278,6 @@ class ApplicationPluginTest : BasePluginTest() { } private companion object { - val runShadow = "tasks.named('$SHADOW_RUN_TASK_NAME')".trim() - fun Path.walkEntries(): Sequence { return walk() .filter { it.isRegularFile() } 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 3f4aee9db..a611a8cde 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 @@ -6,6 +6,7 @@ import assertk.assertThat import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import assertk.assertions.isNotNull +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar @@ -78,8 +79,9 @@ abstract class BasePluginTest { localRepo.root.deleteRecursively() } - open val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" + val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" + val runShadowTask = ":$SHADOW_RUN_TASK_NAME" val projectScriptPath: Path get() = path("build.gradle") val settingsScriptPath: Path get() = path("settings.gradle") @@ -338,6 +340,8 @@ abstract class BasePluginTest { tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name}) """.trimIndent() + val runShadow = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)".trim() + val commonArguments = listOf( "--warning-mode=fail", "--configuration-cache", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt index 077d7b579..2be19e5cc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertFailure import assertk.assertThat +import assertk.assertions.contains import assertk.assertions.isEmpty import assertk.assertions.isInstanceOf import com.github.jengelman.gradle.plugins.shadow.BasePluginTest @@ -16,15 +17,17 @@ import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE abstract class BaseCachingTest : BasePluginTest() { - fun assertFirstExecutionSuccess() { + open val taskPath: String = shadowJarTask + + fun assertExecutionSuccess(vararg outputs: String) { // task was executed and not pulled from cache - assertRunWithOutcome(SUCCESS) + assertRunWithResult(SUCCESS, outputs = outputs) } /** - * This should be called after [assertFirstExecutionSuccess] to ensure that the shadowJar task is cached. + * This should be called after [assertExecutionSuccess] to ensure that the [taskPath] is cached. */ - fun assertExecutionsAreCachedAndUpToDate() { + fun assertExecutionsFromCacheAndUpToDate(vararg outputs: String) { run("clean") // Make sure the output shadow jar has been deleted. assertFailure { outputShadowJar.close() }.isInstanceOf(NoSuchFileException::class) @@ -33,14 +36,15 @@ abstract class BaseCachingTest : BasePluginTest() { // Make sure build folders are deleted by clean task. assertThat(buildDirs).isEmpty() - // check that shadowJar pulls from cache in the original directory - assertRunWithOutcome(FROM_CACHE) - // check that shadowJar pulls from cache in a different directory - assertRunWithOutcome(UP_TO_DATE) + // Run the task again to ensure it is pulled from cache. + assertRunWithResult(FROM_CACHE, outputs = outputs) + // Run the task again to ensure it is up-to-date. + assertRunWithResult(UP_TO_DATE, outputs = outputs) } - private fun assertRunWithOutcome(expectedOutcome: TaskOutcome) { - val result = run(shadowJarTask) - assertThat(result).taskOutcomeEquals(shadowJarTask, expectedOutcome) + private fun assertRunWithResult(expectedOutcome: TaskOutcome, vararg outputs: String) { + val result = run(taskPath) + assertThat(result).taskOutcomeEquals(taskPath, expectedOutcome) + assertThat(result.output).contains(*outputs) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt index 98e73f91a..7c9f9ee8d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt @@ -9,7 +9,7 @@ import kotlin.io.path.writeText import org.junit.jupiter.api.Test class MinimizationCachingTest : BaseCachingTest() { - override val shadowJarTask: String = serverShadowJarTask + override val taskPath: String = serverShadowJarTask override val outputShadowJar: JarPath get() = outputServerShadowJar @Test @@ -22,7 +22,7 @@ class MinimizationCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "server/Server.class", @@ -52,9 +52,9 @@ class MinimizationCachingTest : BaseCachingTest() { } } - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions() } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index eda6b5189..aa440f298 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -21,7 +21,7 @@ class RelocationCachingTest : BaseCachingTest() { ) writeMainClass(withImports = true) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -48,9 +48,9 @@ class RelocationCachingTest : BaseCachingTest() { } } - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions() } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 4d8f569a6..80facbb04 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -25,16 +25,16 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertFirstExecutionSuccess() - assertExecutionsAreCachedAndUpToDate() + assertExecutionSuccess() + assertExecutionsFromCacheAndUpToDate() val replaced = projectScriptPath.readText().lines() .filterNot { it == fromJar(projectJar) } .joinToString(System.lineSeparator()) projectScriptPath.writeText(replaced) - assertFirstExecutionSuccess() - assertExecutionsAreCachedAndUpToDate() + assertExecutionSuccess() + assertExecutionsFromCacheAndUpToDate() } @Test @@ -47,7 +47,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertThat(outputShadowJar).isRegular() projectScriptPath.appendText( @@ -58,7 +58,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertThat(jarPath("build/libs/foo-1.0-all.jar")).isRegular() } @@ -79,7 +79,7 @@ class ShadowJarCachingTest : BaseCachingTest() { ) // First run successful with all files. - assertFirstExecutionSuccess() + assertExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -98,7 +98,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) // Second run successful after excludes changed. - assertFirstExecutionSuccess() + assertExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -119,7 +119,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) // Third run successful after includes changed. - assertFirstExecutionSuccess() + assertExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -140,7 +140,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) // Forth run successful after includes changed again. - assertFirstExecutionSuccess() + assertExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -154,7 +154,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } // Clean and run 2 more times to ensure the states are cached and up-to-date. - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() } @Test @@ -168,7 +168,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertThat(outputShadowJar).useAll { containsEntries( "shadow/Main.class", @@ -196,9 +196,9 @@ class ShadowJarCachingTest : BaseCachingTest() { } } - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions() } @@ -221,9 +221,9 @@ class ShadowJarCachingTest : BaseCachingTest() { } } - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions() projectScriptPath.appendText( @@ -234,9 +234,9 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions() } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 4e0365ea5..e5468842e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -43,7 +43,7 @@ class TransformerCachingTest : BaseCachingTest() { } } - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() projectScriptPath.appendText( @@ -53,19 +53,19 @@ class TransformerCachingTest : BaseCachingTest() { """.trimIndent(), ), ) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions() val replaced = projectScriptPath.readText().replace("META-INF/foo", "META-INF/bar") projectScriptPath.writeText(replaced) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions() } @@ -79,7 +79,7 @@ class TransformerCachingTest : BaseCachingTest() { } } - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions("bar") projectScriptPath.appendText( @@ -89,10 +89,10 @@ class TransformerCachingTest : BaseCachingTest() { """.trimIndent(), ), ) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions("bar") - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions("bar") path("src/main/resources/foo/bar.properties").deleteExisting() @@ -100,10 +100,10 @@ class TransformerCachingTest : BaseCachingTest() { val replaced = projectScriptPath.readText().replace("foo/bar.properties", "foo/baz.properties") projectScriptPath.writeText(replaced) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions("baz") - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions("baz") } @@ -117,7 +117,7 @@ class TransformerCachingTest : BaseCachingTest() { } } - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions("bar") projectScriptPath.appendText( @@ -127,10 +127,10 @@ class TransformerCachingTest : BaseCachingTest() { """.trimIndent(), ), ) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions("bar") - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions("bar") path("src/main/resources/foo/bar.xml").deleteExisting() @@ -138,10 +138,10 @@ class TransformerCachingTest : BaseCachingTest() { val replaced = projectScriptPath.readText().replace("foo/bar.xml", "foo/baz.xml") projectScriptPath.writeText(replaced) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions("baz") - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions("baz") } @@ -158,7 +158,7 @@ class TransformerCachingTest : BaseCachingTest() { } } - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() projectScriptPath.appendText( @@ -168,10 +168,10 @@ class TransformerCachingTest : BaseCachingTest() { } """.trimIndent(), ) - assertFirstExecutionSuccess() + assertExecutionSuccess() assertions() - assertExecutionsAreCachedAndUpToDate() + assertExecutionsFromCacheAndUpToDate() assertions() } From cc89562405314485d6b73b97a70fa743101bb42e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 28 Jan 2025 21:08:56 -0500 Subject: [PATCH 206/941] Add a caching test for runShadow (#1214) * Test runShadowCachedCorrectlyAfterShadowJarChanged * Cleanups * `runTask` is not cacheable, so it's always executed * Rename to runShadowExecutedCorrectlyAfterShadowJarChanged --- .../shadow/caching/ApplicationCachingTest.kt | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt new file mode 100644 index 000000000..51609691d --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt @@ -0,0 +1,81 @@ +package com.github.jengelman.gradle.plugins.shadow.caching + +import kotlin.io.path.appendText +import kotlin.io.path.writeText +import org.junit.jupiter.api.Test + +class ApplicationCachingTest : BaseCachingTest() { + override val taskPath: String = runShadowTask + + @Test + fun runShadowExecutedCorrectlyAfterShadowJarChanged() { + prepare() + val resourcePath = path("src/main/resources/my/resource.txt") + resourcePath.writeText( + """ + Hello, World! %s from resource 1 + """.trimIndent(), + ) + val assertions = { resName: String -> + assertExecutionSuccess("Hello, World! foo from $resName") + // `runTask` is not cacheable, so it's always executed. + assertExecutionSuccess("Hello, World! foo from $resName") + } + + path("src/main/java/my/App.java").writeText( + """ + package my; + public class App { + public static void main(String[] args) throws Exception { + if (args.length == 0) { + throw new IllegalArgumentException("No arguments provided."); + } + var is = App.class.getResourceAsStream("resource.txt"); + String content = String.format(new String(is.readAllBytes()), (Object[]) args); + System.out.println(content); + } + } + """.trimIndent(), + ) + assertions("resource 1") + + resourcePath.writeText( + """ + Hello, World! %s from resource 2 + """.trimIndent(), + ) + assertions("resource 2") + } + + private fun prepare( + projectBlock: String = "", + applicationBlock: String = "", + settingsBlock: String = "", + dependenciesBlock: String = "", + runShadowBlock: String = "", + ) { + projectScriptPath.appendText( + """ + apply plugin: 'application' + $projectBlock + application { + mainClass = 'my.App' + $applicationBlock + } + dependencies { + $dependenciesBlock + } + $runShadow { + args 'foo' + $runShadowBlock + } + """.trimIndent(), + ) + settingsScriptPath.writeText( + getDefaultSettingsBuildScript( + startBlock = settingsBlock, + endBlock = "rootProject.name = 'myapp'", + ), + ) + } +} From 36635714c9e4331c056716d122cf4d77fcd36c84 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 28 Jan 2025 21:41:56 -0500 Subject: [PATCH 207/941] Replace JavaJarExec with JavaExec (#1197) * Remove JavaJarExec * Fix CC * Update changelog * Mark jarFile as a input * Put jarFile into classpath and still use javaApplication.mainClass as mainClass * Update checks in canOverrideMainClassAttrInManifestBlock --- api/shadow.api | 6 ----- src/docs/application-plugin/README.md | 2 +- src/docs/changes/README.md | 4 ++++ .../plugins/shadow/ApplicationPluginTest.kt | 11 +++++++--- .../plugins/shadow/ShadowApplicationPlugin.kt | 16 +++++++------- .../plugins/shadow/tasks/JavaJarExec.kt | 22 ------------------- 6 files changed, 21 insertions(+), 40 deletions(-) delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt diff --git a/api/shadow.api b/api/shadow.api index a87057859..ec3e5ccc3 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -225,12 +225,6 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; } -public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec : org/gradle/api/tasks/JavaExec { - public fun ()V - public fun exec ()V - public abstract fun getJarFile ()Lorg/gradle/api/file/RegularFileProperty; -} - public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask : org/gradle/api/DefaultTask { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Companion; public static final field DESC Ljava/lang/String; diff --git a/src/docs/application-plugin/README.md b/src/docs/application-plugin/README.md index 442f5da01..113bfe118 100644 --- a/src/docs/application-plugin/README.md +++ b/src/docs/application-plugin/README.md @@ -40,7 +40,7 @@ application { mainClass = 'myapp.Main' } -tasks.named('runShadow', com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec) { +tasks.named('runShadow', JavaExec) { args 'foo' } ``` diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 8855acfc6..14e378579 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -19,6 +19,10 @@ - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) - Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) +**Removed** + +- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) + ## [v9.0.0-beta6] (2025-01-23) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 27a1f636a..e38929508 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -1,8 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow +import assertk.all import assertk.assertThat import assertk.assertions.contains import assertk.assertions.containsOnly +import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.DISTRIBUTION_NAME @@ -192,11 +194,14 @@ class ApplicationPluginTest : BasePluginTest() { val result = run(runShadowTask) - assertThat(result.output).contains( - "Hello, World! (foo) from Main2", - ) + assertThat(result.output).all { + // Prefer main class from `application.main` over the one in manifest attributes. + contains("Hello, World! (foo) from Main") + doesNotContain("Hello, World! (foo) from Main2") + } commonAssertions( jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"), + entriesContained = arrayOf("a.properties", "a2.properties", "shadow/Main.class", "shadow/Main2.class"), mainClassAttr = "shadow.Main2", ) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 998abe74d..edc75fe9d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -1,7 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText -import com.github.jengelman.gradle.plugins.shadow.tasks.JavaJarExec import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import org.gradle.api.GradleException import org.gradle.api.Plugin @@ -10,6 +9,7 @@ import org.gradle.api.distribution.DistributionContainer import org.gradle.api.plugins.ApplicationPlugin import org.gradle.api.plugins.JavaApplication import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.Sync import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.application.CreateStartScripts @@ -45,18 +45,18 @@ public abstract class ShadowApplicationPlugin : Plugin { } protected open fun addRunTask() { - project.tasks.register(SHADOW_RUN_TASK_NAME, JavaJarExec::class.java) { + project.tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) { val install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) + val jarFile = install.zip(shadowJar) { i, s -> + i.destinationDir.resolve("lib/${s.archiveFile.get().asFile.name}") + } + it.dependsOn(install) - it.mainClass.set("-jar") + it.classpath(jarFile) + it.mainClass.set(javaApplication.mainClass) it.description = "Runs this project as a JVM application using the shadow jar" it.group = ApplicationPlugin.APPLICATION_GROUP it.conventionMapping.map("jvmArgs") { javaApplication.applicationDefaultJvmArgs } - it.jarFile.fileProvider( - install.zip(shadowJar) { i, s -> - project.file("${i.destinationDir.path}/lib/${s.archiveFile.get().asFile.name}") - }, - ) val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java) .launcherFor(toolchain) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt deleted file mode 100644 index 7e74dc7cd..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/JavaJarExec.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import org.gradle.api.file.RegularFileProperty -import org.gradle.api.tasks.InputFile -import org.gradle.api.tasks.JavaExec -import org.gradle.api.tasks.TaskAction - -public abstract class JavaJarExec : JavaExec() { - @get:InputFile - public abstract val jarFile: RegularFileProperty - - @TaskAction - override fun exec() { - val allArgs = buildList { - add(jarFile.get().asFile.path) - // Must cast args to List here to avoid type mismatch. - addAll(args as List) - } - setArgs(allArgs) - super.exec() - } -} From ac2c5f79836b96afdb33d200f7c61bb5a5cc4155 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 29 Jan 2025 00:08:01 -0500 Subject: [PATCH 208/941] Tidy up ShadowApplicationPlugin (#1198) * Reuse installShadowDist * Remove src/dist from * Revert "Remove src/dist from" This reverts commit 4f6ca4df79d92e632cc795f6cc8476a23ea6df2b. * Cleanups * Test canIncludeSrcDistByDefault * Reuse shadowDistZipTask * Check `run` result in canOverrideMainClassAttrInManifestBlock * Test startShadowScriptsExecutedCorrectlyAfterShadowJarChanged * Revert "Test startShadowScriptsExecutedCorrectlyAfterShadowJarChanged" This reverts commit 9fa14992735049234782271d66a677eedc612ced. --- .../plugins/shadow/ApplicationPluginTest.kt | 51 ++++++++++++--- .../gradle/plugins/shadow/BasePluginTest.kt | 1 + .../plugins/shadow/ShadowApplicationPlugin.kt | 64 ++++++++++--------- 3 files changed, 75 insertions(+), 41 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index e38929508..c0b7f8586 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -110,7 +110,7 @@ class ApplicationPluginTest : BasePluginTest() { dependenciesBlock = "shadow 'junit:junit:3.8.2'", ) - run("shadowDistZip") + run(shadowDistZipTask) val zipPath = path("build/distributions/myapp-shadow-1.0.zip") val extractedPath = path("build/distributions/extracted/") @@ -191,19 +191,29 @@ class ApplicationPluginTest : BasePluginTest() { } """.trimIndent(), ) - - val result = run(runShadowTask) - - assertThat(result.output).all { - // Prefer main class from `application.main` over the one in manifest attributes. - contains("Hello, World! (foo) from Main") - doesNotContain("Hello, World! (foo) from Main2") + val assertions = { output: String, arg: String -> + assertThat(output).all { + // Prefer main class from `application.main` over the one in manifest attributes. + contains("Hello, World! ($arg) from Main") + doesNotContain("Hello, World! ($arg) from Main2") + } } + + assertions(run(runShadowTask).output, "foo") commonAssertions( jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"), entriesContained = arrayOf("a.properties", "a2.properties", "shadow/Main.class", "shadow/Main2.class"), mainClassAttr = "shadow.Main2", ) + + projectScriptPath.appendText( + """ + run { + args 'bar' + } + """.trimIndent(), + ) + assertions(run(":run").output, "bar") } @Test @@ -219,7 +229,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - run("shadowDistZip") + run(shadowDistZipTask) val zipPath = path("build/distributions/myapp-shadow-1.0.zip") ZipFile(zipPath.toFile()).use { zip -> @@ -235,6 +245,27 @@ class ApplicationPluginTest : BasePluginTest() { } } + @Test + fun canIncludeSrcDistByDefault() { + path("src/dist/echo.sh").writeText("echo 'Hello, World!'") + prepare() + + run(shadowDistZipTask) + + val zipPath = path("build/distributions/myapp-shadow-1.0.zip") + ZipFile(zipPath.toFile()).use { zip -> + val entries = zip.entries().toList().filter { !it.isDirectory }.map { it.name } + assertThat(entries).containsOnly( + "myapp-shadow-1.0/bin/myapp", + "myapp-shadow-1.0/bin/myapp.bat", + "myapp-shadow-1.0/lib/myapp-1.0-all.jar", + "myapp-shadow-1.0/echo.sh", + ) + assertThat(zip.getContent("myapp-shadow-1.0/echo.sh")) + .isEqualTo("echo 'Hello, World!'") + } + } + private fun prepare( mainClassWithImports: Boolean = false, projectBlock: String = "", @@ -259,7 +290,7 @@ class ApplicationPluginTest : BasePluginTest() { args 'foo' $runShadowBlock } - """.trimIndent(), + """.trimIndent() + System.lineSeparator(), ) settingsScriptPath.writeText( getDefaultSettingsBuildScript( 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 a611a8cde..62fa1d6ba 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 @@ -82,6 +82,7 @@ abstract class BasePluginTest { val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" val runShadowTask = ":$SHADOW_RUN_TASK_NAME" + val shadowDistZipTask = ":shadowDistZip" val projectScriptPath: Path get() = path("build.gradle") val settingsScriptPath: Path get() = path("settings.gradle") diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index edc75fe9d..a8941bf87 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -33,61 +33,60 @@ public abstract class ShadowApplicationPlugin : Plugin { protected open fun configureJarMainClass() { val classNameProvider = javaApplication.mainClass - shadowJar.configure { jar -> - jar.inputs.property("mainClassName", classNameProvider) - jar.doFirst { + shadowJar.configure { task -> + task.inputs.property("mainClassName", classNameProvider) + task.doFirst { // Inject the Main-Class attribute if it is not already present. - if (!jar.manifest.attributes.contains("Main-Class")) { - jar.manifest.attributes["Main-Class"] = classNameProvider.get() + if (!task.manifest.attributes.contains("Main-Class")) { + task.manifest.attributes["Main-Class"] = classNameProvider.get() } } } } protected open fun addRunTask() { - project.tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) { - val install = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) - val jarFile = install.zip(shadowJar) { i, s -> + project.tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) { task -> + task.description = "Runs this project as a JVM application using the shadow jar" + task.group = ApplicationPlugin.APPLICATION_GROUP + task.dependsOn(installShadowDist) + + val jarFile = installShadowDist.zip(shadowJar) { i, s -> i.destinationDir.resolve("lib/${s.archiveFile.get().asFile.name}") } - - it.dependsOn(install) - it.classpath(jarFile) - it.mainClass.set(javaApplication.mainClass) - it.description = "Runs this project as a JVM application using the shadow jar" - it.group = ApplicationPlugin.APPLICATION_GROUP - it.conventionMapping.map("jvmArgs") { javaApplication.applicationDefaultJvmArgs } + task.classpath(jarFile) + task.mainClass.set(javaApplication.mainClass) + task.conventionMapping.map("jvmArgs", javaApplication::getApplicationDefaultJvmArgs) val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain - val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java) - .launcherFor(toolchain) - it.javaLauncher.set(defaultLauncher) + val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java).launcherFor(toolchain) + task.javaLauncher.set(defaultLauncher) } } protected open fun addCreateScriptsTask() { - project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { + project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { task -> + task.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" + task.group = ApplicationPlugin.APPLICATION_GROUP + val unixStartScript = requireResourceAsText("com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt") val windowsStartScript = requireResourceAsText("com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt") - - (it.unixStartScriptGenerator as TemplateBasedScriptGenerator).template = + (task.unixStartScriptGenerator as TemplateBasedScriptGenerator).template = project.resources.text.fromString(unixStartScript) - (it.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = + (task.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = project.resources.text.fromString(windowsStartScript) - it.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" - it.group = ApplicationPlugin.APPLICATION_GROUP - it.classpath = project.files(shadowJar) - it.mainClass.set(javaApplication.mainClass) - it.conventionMapping.map("applicationName") { javaApplication.applicationName } - it.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile } - it.conventionMapping.map("defaultJvmOpts") { javaApplication.applicationDefaultJvmArgs } - it.inputs.files(project.files(shadowJar)) + + task.classpath = project.files(shadowJar) + task.inputs.files(project.files(shadowJar)) + task.mainClass.set(javaApplication.mainClass) + task.conventionMapping.map("applicationName", javaApplication::getApplicationName) + task.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile } + task.conventionMapping.map("defaultJvmOpts", javaApplication::getApplicationDefaultJvmArgs) } } protected open fun configureInstallTask() { - project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java).configure { task -> + installShadowDist.configure { task -> val applicationName = project.providers.provider { javaApplication.applicationName } task.doFirst { @@ -135,6 +134,9 @@ public abstract class ShadowApplicationPlugin : Plugin { protected val shadowJar: TaskProvider get() = project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar::class.java) + private val installShadowDist: TaskProvider + get() = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) + public companion object { public const val SHADOW_RUN_TASK_NAME: String = "runShadow" public const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" From 046dce55a66d553532c6295072b0ee97c668523b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 29 Jan 2025 00:29:16 -0500 Subject: [PATCH 209/941] Polish startShadowScripts registering (#1216) * Update startShadowScripts task logic Sync with https://github.com/gradle/gradle/blob/bcecbb416f19438c7532e309456e3c3ed287f8f5/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java#L184-L203 * Update changelog --- src/docs/changes/README.md | 1 + .../gradle/plugins/shadow/ShadowApplicationPlugin.kt | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 14e378579..132d78633 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -13,6 +13,7 @@ - Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) - Mark `ShadowJar.dependencyFilter` as `@Input`. ([#1206](https://github.com/GradleUp/shadow/pull/1206)) `ShadowSpec.stats` is removed and `ShadowJar.stats` is `internal` for now. +- Polish `startShadowScripts` task registering. ([#1216](https://github.com/GradleUp/shadow/pull/1216)) **Fixed** diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index a8941bf87..9067f65b4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -62,6 +62,9 @@ public abstract class ShadowApplicationPlugin : Plugin { } } + /** + * Syncs with [ApplicationPlugin.addCreateScriptsTask](https://github.com/gradle/gradle/blob/bcecbb416f19438c7532e309456e3c3ed287f8f5/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java#L184-L203). + */ protected open fun addCreateScriptsTask() { project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { task -> task.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" @@ -77,11 +80,14 @@ public abstract class ShadowApplicationPlugin : Plugin { project.resources.text.fromString(windowsStartScript) task.classpath = project.files(shadowJar) - task.inputs.files(project.files(shadowJar)) + task.mainModule.set(javaApplication.mainModule) task.mainClass.set(javaApplication.mainClass) task.conventionMapping.map("applicationName", javaApplication::getApplicationName) task.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile } + task.conventionMapping.map("executableDir", javaApplication::getExecutableDir) task.conventionMapping.map("defaultJvmOpts", javaApplication::getApplicationDefaultJvmArgs) + val javaPluginExtension = project.extensions.getByType(JavaPluginExtension::class.java) + task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) } } From 6a0bca86dbd0f272a6bcf23c5fdda10fd4c90b43 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 29 Jan 2025 05:08:38 -0500 Subject: [PATCH 210/941] Use more extensions on TaskContainer and Project (#1217) * Add extensions for project ExtensionContainer * Add extensions for project TaskContainer * Remove explicit dependsOn * Move configuring logic into project extensions * Simplify ShadowPlugin.apply * Cleanups --- api/shadow.api | 20 ++- .../plugins/shadow/ShadowApplicationPlugin.kt | 128 ++++++++---------- .../gradle/plugins/shadow/ShadowBasePlugin.kt | 6 + .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 128 ++++++++++-------- .../gradle/plugins/shadow/ShadowPlugin.kt | 14 +- .../plugins/shadow/internal/GradleCompat.kt | 26 ++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 5 +- 7 files changed, 186 insertions(+), 141 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index ec3e5ccc3..4e38af587 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -4,17 +4,18 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowApplicati public static final field SHADOW_RUN_TASK_NAME Ljava/lang/String; public static final field SHADOW_SCRIPTS_TASK_NAME Ljava/lang/String; public fun ()V - protected fun addCreateScriptsTask ()V - protected fun addRunTask ()V + protected fun addCreateScriptsTask (Lorg/gradle/api/Project;)V + protected fun addRunTask (Lorg/gradle/api/Project;)V public synthetic fun apply (Ljava/lang/Object;)V public fun apply (Lorg/gradle/api/Project;)V - protected fun configureDistSpec ()V - protected fun configureInstallTask ()V - protected fun configureJarMainClass ()V - protected final fun getShadowJar ()Lorg/gradle/api/tasks/TaskProvider; + protected fun configureDistSpec (Lorg/gradle/api/Project;)V + protected fun configureInstallTask (Lorg/gradle/api/Project;)V + protected fun configureJarMainClass (Lorg/gradle/api/Project;)V } public final class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion { + public final fun getInstallShadowDist (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; + public final fun getStartShadowScripts (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin : org/gradle/api/Plugin { @@ -31,6 +32,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugi } public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$Companion { + public final fun getShadow (Lorg/gradle/api/artifacts/ConfigurationContainer;)Lorg/gradle/api/NamedDomainObjectProvider; } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { @@ -45,9 +47,15 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugi public fun (Lorg/gradle/api/component/SoftwareComponentFactory;)V public synthetic fun apply (Ljava/lang/Object;)V public fun apply (Lorg/gradle/api/Project;)V + protected fun configureComponents (Lorg/gradle/api/Project;)V + 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 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 abstract class com/github/jengelman/gradle/plugins/shadow/ShadowPlugin : org/gradle/api/Plugin { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 9067f65b4..c4bb1bcac 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -1,99 +1,90 @@ package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow +import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.shadowJar +import com.github.jengelman.gradle.plugins.shadow.internal.applicationExtension +import com.github.jengelman.gradle.plugins.shadow.internal.distributions +import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension +import com.github.jengelman.gradle.plugins.shadow.internal.javaToolchainService import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.distribution.DistributionContainer import org.gradle.api.plugins.ApplicationPlugin -import org.gradle.api.plugins.JavaApplication -import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.Sync +import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.application.CreateStartScripts import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator -import org.gradle.jvm.toolchain.JavaToolchainService public abstract class ShadowApplicationPlugin : Plugin { - private lateinit var project: Project - private lateinit var javaApplication: JavaApplication - override fun apply(project: Project) { - this.project = project - this.javaApplication = project.extensions.getByType(JavaApplication::class.java) - - addRunTask() - addCreateScriptsTask() - configureDistSpec() - configureJarMainClass() - configureInstallTask() + project.addRunTask() + project.addCreateScriptsTask() + project.configureDistSpec() + project.configureJarMainClass() + project.configureInstallTask() } - protected open fun configureJarMainClass() { - val classNameProvider = javaApplication.mainClass - shadowJar.configure { task -> - task.inputs.property("mainClassName", classNameProvider) + protected open fun Project.configureJarMainClass() { + val mainClassName = applicationExtension.mainClass + tasks.shadowJar.configure { task -> + task.inputs.property("mainClassName", mainClassName) task.doFirst { // Inject the Main-Class attribute if it is not already present. if (!task.manifest.attributes.contains("Main-Class")) { - task.manifest.attributes["Main-Class"] = classNameProvider.get() + task.manifest.attributes["Main-Class"] = mainClassName.get() } } } } - protected open fun addRunTask() { - project.tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) { task -> + protected open fun Project.addRunTask() { + val extension = applicationExtension + tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) { task -> task.description = "Runs this project as a JVM application using the shadow jar" task.group = ApplicationPlugin.APPLICATION_GROUP - task.dependsOn(installShadowDist) - val jarFile = installShadowDist.zip(shadowJar) { i, s -> + val jarFile = tasks.installShadowDist.zip(tasks.shadowJar) { i, s -> i.destinationDir.resolve("lib/${s.archiveFile.get().asFile.name}") } task.classpath(jarFile) - task.mainClass.set(javaApplication.mainClass) - task.conventionMapping.map("jvmArgs", javaApplication::getApplicationDefaultJvmArgs) - val toolchain = project.extensions.getByType(JavaPluginExtension::class.java).toolchain - val defaultLauncher = project.extensions.getByType(JavaToolchainService::class.java).launcherFor(toolchain) - task.javaLauncher.set(defaultLauncher) + task.mainClass.set(extension.mainClass) + task.conventionMapping.map("jvmArgs", extension::getApplicationDefaultJvmArgs) + task.javaLauncher.set(javaToolchainService.launcherFor(javaPluginExtension.toolchain)) } } /** * Syncs with [ApplicationPlugin.addCreateScriptsTask](https://github.com/gradle/gradle/blob/bcecbb416f19438c7532e309456e3c3ed287f8f5/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java#L184-L203). */ - protected open fun addCreateScriptsTask() { - project.tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { task -> + protected open fun Project.addCreateScriptsTask() { + val extension = applicationExtension + tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { task -> task.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" task.group = ApplicationPlugin.APPLICATION_GROUP - val unixStartScript = - requireResourceAsText("com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt") - val windowsStartScript = - requireResourceAsText("com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt") + val dir = "com/github/jengelman/gradle/plugins/shadow/internal" (task.unixStartScriptGenerator as TemplateBasedScriptGenerator).template = - project.resources.text.fromString(unixStartScript) + resources.text.fromString(requireResourceAsText("$dir/unixStartScript.txt")) (task.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = - project.resources.text.fromString(windowsStartScript) - - task.classpath = project.files(shadowJar) - task.mainModule.set(javaApplication.mainModule) - task.mainClass.set(javaApplication.mainClass) - task.conventionMapping.map("applicationName", javaApplication::getApplicationName) - task.conventionMapping.map("outputDir") { project.layout.buildDirectory.dir("scriptsShadow").get().asFile } - task.conventionMapping.map("executableDir", javaApplication::getExecutableDir) - task.conventionMapping.map("defaultJvmOpts", javaApplication::getApplicationDefaultJvmArgs) - val javaPluginExtension = project.extensions.getByType(JavaPluginExtension::class.java) + resources.text.fromString(requireResourceAsText("$dir/windowsStartScript.txt")) + + task.classpath = files(tasks.shadowJar) + task.mainModule.set(extension.mainModule) + task.mainClass.set(extension.mainClass) + task.conventionMapping.map("applicationName", extension::getApplicationName) + task.conventionMapping.map("outputDir") { layout.buildDirectory.dir("scriptsShadow").get().asFile } + task.conventionMapping.map("executableDir", extension::getExecutableDir) + task.conventionMapping.map("defaultJvmOpts", extension::getApplicationDefaultJvmArgs) task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) } } - protected open fun configureInstallTask() { - installShadowDist.configure { task -> - val applicationName = project.providers.provider { javaApplication.applicationName } + protected open fun Project.configureInstallTask() { + tasks.installShadowDist.configure { task -> + val applicationName = providers.provider { applicationExtension.applicationName } task.doFirst { if ( @@ -120,32 +111,31 @@ public abstract class ShadowApplicationPlugin : Plugin { } } - protected open fun configureDistSpec() { - project.extensions.getByType(DistributionContainer::class.java) - .register(ShadowBasePlugin.DISTRIBUTION_NAME) { distributions -> - distributions.contents { contents -> - contents.from(project.file("src/dist")) - contents.into("lib") { lib -> - lib.from(shadowJar) - lib.from(project.configurations.named(ShadowBasePlugin.CONFIGURATION_NAME)) - } - contents.into("bin") { bin -> - bin.from(project.tasks.named(SHADOW_SCRIPTS_TASK_NAME)) - bin.filePermissions { it.unix(493) } - } + protected open fun Project.configureDistSpec() { + distributions.register(ShadowBasePlugin.DISTRIBUTION_NAME) { distributions -> + distributions.contents { contents -> + contents.from(file("src/dist")) + contents.into("lib") { lib -> + lib.from(tasks.shadowJar) + lib.from(configurations.shadow) + } + contents.into("bin") { bin -> + bin.from(tasks.startShadowScripts) + bin.filePermissions { it.unix(493) } } } + } } - protected val shadowJar: TaskProvider - get() = project.tasks.named(ShadowJavaPlugin.SHADOW_JAR_TASK_NAME, ShadowJar::class.java) - - private val installShadowDist: TaskProvider - get() = project.tasks.named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) - public companion object { public const val SHADOW_RUN_TASK_NAME: String = "runShadow" public const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" public const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" + + public inline val TaskContainer.startShadowScripts: TaskProvider + get() = named(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) + + public inline val TaskContainer.installShadowDist: TaskProvider + get() = named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index bdf44ea48..dd03e4387 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -2,8 +2,11 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.tasks.KnowsTask import org.gradle.api.GradleException +import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.util.GradleVersion public abstract class ShadowBasePlugin : Plugin { @@ -27,5 +30,8 @@ public abstract class ShadowBasePlugin : Plugin { public const val CONFIGURATION_NAME: String = SHADOW public const val COMPONENT_NAME: String = SHADOW public const val DISTRIBUTION_NAME: String = SHADOW + + public inline val ConfigurationContainer.shadow: NamedDomainObjectProvider + get() = named(CONFIGURATION_NAME) } } 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 93e313af0..e2e350fc6 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 @@ -1,11 +1,17 @@ package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow +import com.github.jengelman.gradle.plugins.shadow.internal.jar +import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension 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.NamedDomainObjectProvider import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ConfigurationContainer import org.gradle.api.attributes.Bundling import org.gradle.api.attributes.Category import org.gradle.api.attributes.LibraryElements @@ -14,10 +20,8 @@ import org.gradle.api.attributes.java.TargetJvmVersion import org.gradle.api.component.AdhocComponentWithVariants import org.gradle.api.component.SoftwareComponentFactory import org.gradle.api.plugins.JavaPlugin -import org.gradle.api.plugins.JavaPluginExtension -import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider -import org.gradle.jvm.tasks.Jar import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin public abstract class ShadowJavaPlugin @Inject constructor( @@ -25,95 +29,107 @@ public abstract class ShadowJavaPlugin @Inject constructor( ) : Plugin { override fun apply(project: Project) { - val shadowConfiguration = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) - val shadowTaskProvider = configureShadowTask(project, shadowConfiguration) + project.configureShadowJar() + project.configureConfigurations() + project.configureComponents() + project.configureJavaGradlePlugin() + } - project.configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { - it.extendsFrom(shadowConfiguration) + 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") + @Suppress("EagerGradleConfiguration") + task.manifest.inheritFrom(jarTask.get().manifest) + val attrProvider = jarTask.map { it.manifest.attributes["Class-Path"]?.toString().orEmpty() } + val files = files(configurations.shadow) + task.doFirst { + if (!files.isEmpty) { + val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } + task.manifest.attributes["Class-Path"] = 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", + "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) + } - val shadowRuntimeElements = project.configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { + protected open fun Project.configureConfigurations() { + val shadowConfiguration = configurations.shadow.get() + configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { + it.extendsFrom(shadowConfiguration) + } + configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { it.extendsFrom(shadowConfiguration) it.isCanBeConsumed = true it.isCanBeResolved = false it.attributes { attr -> - attr.attribute(Usage.USAGE_ATTRIBUTE, project.objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) - attr.attribute(Category.CATEGORY_ATTRIBUTE, project.objects.named(Category::class.java, Category.LIBRARY)) + attr.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) + attr.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, Category.LIBRARY)) attr.attribute( LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, - project.objects.named(LibraryElements::class.java, LibraryElements.JAR), + objects.named(LibraryElements::class.java, LibraryElements.JAR), ) - attr.attribute(Bundling.BUNDLING_ATTRIBUTE, project.objects.named(Bundling::class.java, Bundling.SHADOWED)) - val targetJvmVersion = project.provider { - project.extensions.getByType(JavaPluginExtension::class.java).targetCompatibility.majorVersion.toInt() + attr.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling::class.java, Bundling.SHADOWED)) + val targetJvmVersion = provider { + javaPluginExtension.targetCompatibility.majorVersion.toInt() } // Track JavaPluginExtension to update targetJvmVersion when it changes. attr.attributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) } - it.outgoing.artifact(shadowTaskProvider) + it.outgoing.artifact(tasks.shadowJar) } + } - project.components.named("java", AdhocComponentWithVariants::class.java) { + protected open fun Project.configureComponents() { + val shadowRuntimeElements = configurations.shadowRuntimeElements.get() + components.named("java", AdhocComponentWithVariants::class.java) { it.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> variant.mapToOptional() } } - val shadowComponent = softwareComponentFactory.adhoc(ShadowBasePlugin.COMPONENT_NAME) - project.components.add(shadowComponent) + components.add(shadowComponent) shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> variant.mapToMavenScope("runtime") } + } - project.plugins.withType(JavaGradlePluginPlugin::class.java).configureEach { + protected open fun Project.configureJavaGradlePlugin() { + plugins.withType(JavaGradlePluginPlugin::class.java).configureEach { // Remove the gradleApi so it isn't merged into the jar file. // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/de - project.configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { - it.dependencies.remove(project.dependencies.gradleApi()) + configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { + it.dependencies.remove(dependencies.gradleApi()) } // Compile only gradleApi() to make sure the plugin can compile against Gradle API. - project.configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) { - it.dependencies.add(project.dependencies.gradleApi()) - } - } - } - - private fun configureShadowTask(project: Project, shadowConfiguration: Configuration): TaskProvider { - val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) - val jarTask = project.tasks.named(JavaPlugin.JAR_TASK_NAME, Jar::class.java) - val taskProvider = project.tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { shadow -> - shadow.group = ShadowBasePlugin.GROUP_NAME - shadow.description = "Create a combined JAR of project and runtime dependencies" - shadow.archiveClassifier.set("all") - @Suppress("EagerGradleConfiguration") - shadow.manifest.inheritFrom(jarTask.get().manifest) - val attrProvider = jarTask.map { it.manifest.attributes["Class-Path"]?.toString().orEmpty() } - val files = project.files(shadowConfiguration) - shadow.doFirst { - if (!files.isEmpty) { - val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } - shadow.manifest.attributes["Class-Path"] = attrs.joinToString(" ").trim() - } + configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) { + it.dependencies.add(dependencies.gradleApi()) } - shadow.from(sourceSets.named("main").map { it.output }) - shadow.configurations.convention(listOf(project.runtimeConfiguration)) - shadow.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", - ) } - project.artifacts.add(shadowConfiguration.name, taskProvider) - return taskProvider } public companion object { public const val SHADOW_JAR_TASK_NAME: String = "shadowJar" public const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" + + public inline val TaskContainer.shadowJar: TaskProvider + get() = named(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) + + public inline val ConfigurationContainer.shadowRuntimeElements: NamedDomainObjectProvider + get() = named(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) } } 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 e0cde72f2..f89b51377 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 @@ -9,22 +9,22 @@ import org.gradle.api.plugins.JavaPlugin public abstract class ShadowPlugin : Plugin { override fun apply(project: Project) { - project.run { - plugins.apply(ShadowBasePlugin::class.java) + with(project.plugins) { + apply(ShadowBasePlugin::class.java) @Suppress("WithTypeWithoutConfigureEach") - plugins.withType(JavaPlugin::class.java) { - plugins.apply(ShadowJavaPlugin::class.java) + withType(JavaPlugin::class.java) { + apply(ShadowJavaPlugin::class.java) } @Suppress("WithTypeWithoutConfigureEach") - plugins.withType(ApplicationPlugin::class.java) { - plugins.apply(ShadowApplicationPlugin::class.java) + withType(ApplicationPlugin::class.java) { + apply(ShadowApplicationPlugin::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. // If the user applies shadow before those plugins. However, this is fine, because this was also // the behavior with the old plugin when applying in that order. - plugins.apply(LegacyShadowPlugin::class.java) + apply(LegacyShadowPlugin::class.java) } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index 0061d60e7..e7e7299b8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -3,13 +3,21 @@ package com.github.jengelman.gradle.plugins.shadow.internal import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.distribution.DistributionContainer import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.JavaApplication import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider +import org.gradle.jvm.tasks.Jar +import org.gradle.jvm.toolchain.JavaToolchainService import org.gradle.util.GradleVersion /** @@ -21,6 +29,24 @@ internal inline val Project.runtimeConfiguration: Configuration ?: configurations.getByName("runtime") } +internal inline val Project.sourceSets: SourceSetContainer + get() = extensions.getByType(SourceSetContainer::class.java) + +internal inline val Project.distributions: DistributionContainer + get() = extensions.getByType(DistributionContainer::class.java) + +internal inline val Project.applicationExtension: JavaApplication + get() = extensions.getByType(JavaApplication::class.java) + +internal inline val Project.javaPluginExtension: JavaPluginExtension + get() = extensions.getByType(JavaPluginExtension::class.java) + +internal inline val Project.javaToolchainService: JavaToolchainService + get() = extensions.getByType(JavaToolchainService::class.java) + +internal inline val TaskContainer.jar: TaskProvider + get() = named("jar", Jar::class.java) + internal inline fun ObjectFactory.property( defaultValue: Any? = null, ): Property = property(V::class.java).apply { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 214c99ac9..4f1edc074 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -11,6 +11,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.fileCollection import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.setProperty +import com.github.jengelman.gradle.plugins.shadow.internal.sourceSets import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator @@ -43,7 +44,6 @@ import org.gradle.api.tasks.Nested import org.gradle.api.tasks.Optional import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity -import org.gradle.api.tasks.SourceSetContainer import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.bundling.ZipEntryCompression @@ -94,8 +94,7 @@ public abstract class ShadowJar : public open val sourceSetsClassesDirs: ConfigurableFileCollection = objectFactory.fileCollection { minimizeJar.map { if (it) { - project.extensions.getByType(SourceSetContainer::class.java) - .map { sourceSet -> sourceSet.output.classesDirs.filter(File::isDirectory) } + project.sourceSets.map { sourceSet -> sourceSet.output.classesDirs.filter(File::isDirectory) } } else { emptySet() } From d64ef3787ca0298addd7cb4dbd4beb49814b2bee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Jan 2025 13:47:36 +0000 Subject: [PATCH 211/941] Update dependency com.gradle.publish:plugin-publish-plugin to v1.3.1 (#1219) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dcf70fd28..2b9490604 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,7 +16,7 @@ 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.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" From ea5d873e2cefe90ad918571d097da228cd498082 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 2 Feb 2025 17:49:01 +0800 Subject: [PATCH 212/941] Remove MaxMetaspaceSize option --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c67807733..747a90b12 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ kotlin.stdlib.default.dependency=false org.gradle.caching=true org.gradle.configuration-cache=true org.gradle.configuration-cache.parallel=true -org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g -XX:MaxMetaspaceSize=2g +org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g org.gradle.kotlin.dsl.allWarningsAsErrors=true org.gradle.parallel=true From c24a57051cc2e46b68464a766dc0a7e9d3fcd351 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 3 Feb 2025 01:34:31 +0800 Subject: [PATCH 213/941] Update dependency org.vafer:jdependency to v2.12 (#1222) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2b9490604..72dcd239e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ apache-commonsIo = "commons-io:commons-io:2.18.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.9" asm = "org.ow2.asm:asm-commons:9.7.1" -jdependency = "org.vafer:jdependency:2.11" +jdependency = "org.vafer:jdependency:2.12" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" From 50f92ddc12fe57ed3ed34adcc2fa0dbca3ea7a25 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 3 Feb 2025 02:14:18 +0800 Subject: [PATCH 214/941] Prepare changelog for version 8.3.6 (cherry picked from commit 360e9dbf41214791de71fb7477c10a6b849d2395) --- src/docs/changes/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 132d78633..cef039e36 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -6,6 +6,7 @@ **Added** - Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) +- Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) **Changed** @@ -99,6 +100,17 @@ - Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) +## [v8.3.6] (2025-02-02) + +**Added** + +- Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) + +**Changed** + +- Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) + + ## [v8.3.5] (2024-11-03) **Fixed** @@ -517,6 +529,7 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Mon, 3 Feb 2025 02:26:26 +0800 Subject: [PATCH 215/941] Prepare version 9.0.0-beta7 --- gradle.properties | 2 +- src/docs/changes/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 747a90b12..e7c22ed5d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta7 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index cef039e36..dc4357eb8 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased] + +## [v9.0.0-beta7] (2025-02-02) + **Added** - Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) @@ -522,7 +525,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Mon, 3 Feb 2025 02:27:42 +0800 Subject: [PATCH 216/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index e7c22ed5d..41073c779 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta7 +VERSION_NAME=9.0.0-beta8-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 8cb05300396993749e814d7787249ae738a789b0 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 4 Feb 2025 11:54:08 +0800 Subject: [PATCH 217/941] Note PR 1093 for 9.0.0-beta5 changes --- src/docs/changes/README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index dc4357eb8..56a3ac36b 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -48,6 +48,7 @@ **Changed** +- Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) - **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) Some public `List` parameters are also changed to `Set`. - Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) @@ -109,10 +110,6 @@ - Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) -**Changed** - -- Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) - ## [v8.3.5] (2024-11-03) From 9b6a446fd569ac7f143df9b3683275c3a9f1a166 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 5 Feb 2025 13:46:37 +0800 Subject: [PATCH 218/941] Revert "Configure dependabot (#1193)" This reverts commit a9b6f46c7472998b5fe906da345745d36d059e58. --- .github/dependabot.yml | 11 ----------- .github/workflows/ci.yml | 4 ---- 2 files changed, 15 deletions(-) delete mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml deleted file mode 100644 index 5f0889ce9..000000000 --- a/.github/dependabot.yml +++ /dev/null @@ -1,11 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for all configuration options: -# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file - -version: 2 -updates: - - package-ecosystem: "npm" # See documentation for possible values - directory: "/" # Location of package manifests - schedule: - interval: "weekly" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 086ebbc43..38b7e36f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,11 +4,7 @@ on: push: branches: - main - paths-ignore: - - yarn.lock pull_request: - paths-ignore: - - yarn.lock workflow_dispatch: jobs: From 902f2aea3bc2a68c5247449b80ea7cbdfe3ec8a5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 5 Feb 2025 02:29:10 -0500 Subject: [PATCH 219/941] Sync ShadowApplicationPlugin with ApplicationPlugin (#1224) * Update addRunTask * Note kdoc for ShadowApplicationPlugin * Update configureDistribution and cleanups * Note 755 * Test `application.applicationDistribution` usage * Update changelog * Remove redundant `shadowApplicationDistributionsShouldUseShadowJar` Syncs with https://github.com/gradle/gradle/blob/45a20d82b623786d19b50185e595adf3d7b196b2/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java. --- api/shadow.api | 4 +- src/docs/application-plugin/README.md | 9 ++- src/docs/changes/README.md | 4 + .../plugins/shadow/ApplicationPluginTest.kt | 78 +++---------------- .../plugins/shadow/ShadowApplicationPlugin.kt | 65 +++++++++------- 5 files changed, 60 insertions(+), 100 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 4e38af587..4ff35dff6 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -8,9 +8,9 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowApplicati protected fun addRunTask (Lorg/gradle/api/Project;)V public synthetic fun apply (Ljava/lang/Object;)V public fun apply (Lorg/gradle/api/Project;)V - protected fun configureDistSpec (Lorg/gradle/api/Project;)V + protected fun configureDistribution (Lorg/gradle/api/Project;)V protected fun configureInstallTask (Lorg/gradle/api/Project;)V - protected fun configureJarMainClass (Lorg/gradle/api/Project;)V + protected fun configureShadowJarMainClass (Lorg/gradle/api/Project;)V } public final class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion { diff --git a/src/docs/application-plugin/README.md b/src/docs/application-plugin/README.md index 113bfe118..3d2765d19 100644 --- a/src/docs/application-plugin/README.md +++ b/src/docs/application-plugin/README.md @@ -38,6 +38,8 @@ plugins { application { mainClass = 'myapp.Main' + // Optionally, you can add default JVM arguments to the start scripts like this: + applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] } tasks.named('runShadow', JavaExec) { @@ -68,12 +70,15 @@ plugins { application { mainClass = 'myapp.Main' - // Optionally, you can add default JVM arguments to the start scripts like this: - applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] + // Optionally, you can include `some/dir` files in the distribution like this: + applicationDistribution.from('some/dir') { + include '*.txt' + } } // `shadow` is the name of the distribution created by Shadow plugin distributions.named('shadow') { + // Optionally, you can add more files into extra directory in the distribution like this: contents.into('extra') { from project.file('extra/echo.sh') } diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 56a3ac36b..cfe199e41 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Added** + +- Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) + ## [v9.0.0-beta7] (2025-02-02) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index c0b7f8586..a4e00e9b3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -20,9 +20,7 @@ import java.nio.file.Path import java.util.zip.ZipFile import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.appendText -import kotlin.io.path.createDirectories import kotlin.io.path.isRegularFile -import kotlin.io.path.outputStream import kotlin.io.path.readText import kotlin.io.path.relativeTo import kotlin.io.path.walk @@ -100,73 +98,6 @@ class ApplicationPluginTest : BasePluginTest() { ) } - @Test - fun shadowApplicationDistributionsShouldUseShadowJar() { - prepare( - mainClassWithImports = true, - applicationBlock = """ - applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] - """.trimIndent(), - dependenciesBlock = "shadow 'junit:junit:3.8.2'", - ) - - run(shadowDistZipTask) - - val zipPath = path("build/distributions/myapp-shadow-1.0.zip") - val extractedPath = path("build/distributions/extracted/") - - // Unzip the whole file. - ZipFile(zipPath.toFile()).use { zip -> - zip.entries().toList().forEach { entry -> - val entryDest = extractedPath.resolve(entry.name) - if (entry.isDirectory) { - entryDest.createDirectories() - } else { - zip.getInputStream(entry).use { it.copyTo(entryDest.outputStream()) } - } - } - } - - assertThat(extractedPath.walkEntries()).containsOnly( - "myapp-shadow-1.0/bin/myapp", - "myapp-shadow-1.0/bin/myapp.bat", - "myapp-shadow-1.0/lib/myapp-1.0-all.jar", - "myapp-shadow-1.0/lib/junit-3.8.2.jar", - ) - - commonAssertions( - jarPath("myapp-shadow-1.0/lib/myapp-1.0-all.jar", extractedPath), - entriesContained = arrayOf("shadow/Main.class"), - classPathAttr = "junit-3.8.2.jar", - ) - - val unixScript = path("myapp-shadow-1.0/bin/myapp", extractedPath) - val winScript = path("myapp-shadow-1.0/bin/myapp.bat", extractedPath) - - assertThat(unixScript.readText()).contains( - "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", - "exec \"\$JAVACMD\" \"\$@\"", - "DEFAULT_JVM_OPTS='\"--add-opens=java.base/java.lang=ALL-UNNAMED\"'", - ) - assertThat(winScript.readText()).contains( - "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", - "set DEFAULT_JVM_OPTS=\"--add-opens=java.base/java.lang=ALL-UNNAMED\"", - ) - - val runningOutput = if (isWindows) { - // Use `cmd` and `/c` explicitly to prevent `The process cannot access the file because it is being used by another process` errors. - runProcess("cmd", "/c", winScript.toString(), "bar") - } else { - // Use `sh` explicitly since the extracted script is non-executable. - // Avoid `chmod +x` to prevent `Text file busy` errors on Linux. - runProcess("sh", unixScript.toString(), "bar") - } - assertThat(runningOutput).contains( - "Hello, World! (bar) from Main", - "Refs: junit.framework.Test", - ) - } - @Test fun installShadowDoesNotExecuteDependentShadowTask() { prepare() @@ -219,6 +150,7 @@ class ApplicationPluginTest : BasePluginTest() { @Test fun canAddExtraFilesIntoDistribution() { path("extra/echo.sh").writeText("echo 'Hello, World!'") + path("some/dir/hello.txt").writeText("'Hello, World!'") prepare( projectBlock = """ distributions.named('$DISTRIBUTION_NAME') { @@ -227,6 +159,11 @@ class ApplicationPluginTest : BasePluginTest() { } } """.trimIndent(), + applicationBlock = """ + applicationDistribution.from('some/dir') { + include '*.txt' + } + """.trimIndent(), ) run(shadowDistZipTask) @@ -239,9 +176,12 @@ class ApplicationPluginTest : BasePluginTest() { "myapp-shadow-1.0/bin/myapp.bat", "myapp-shadow-1.0/lib/myapp-1.0-all.jar", "myapp-shadow-1.0/extra/echo.sh", + "myapp-shadow-1.0/hello.txt", ) assertThat(zip.getContent("myapp-shadow-1.0/extra/echo.sh")) .isEqualTo("echo 'Hello, World!'") + assertThat(zip.getContent("myapp-shadow-1.0/hello.txt")) + .isEqualTo("'Hello, World!'") } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index c4bb1bcac..74034b7b4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -18,28 +18,20 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.application.CreateStartScripts import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator +/** + * A [Plugin] which packages and runs a project as a Java Application using the shadowed jar. + * + * Modified from [org.gradle.api.plugins.ApplicationPlugin.java](https://github.com/gradle/gradle/blob/45a20d82b623786d19b50185e595adf3d7b196b2/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java). + */ public abstract class ShadowApplicationPlugin : Plugin { override fun apply(project: Project) { project.addRunTask() project.addCreateScriptsTask() - project.configureDistSpec() - project.configureJarMainClass() + project.configureDistribution() + project.configureShadowJarMainClass() project.configureInstallTask() } - protected open fun Project.configureJarMainClass() { - val mainClassName = applicationExtension.mainClass - tasks.shadowJar.configure { task -> - task.inputs.property("mainClassName", mainClassName) - task.doFirst { - // Inject the Main-Class attribute if it is not already present. - if (!task.manifest.attributes.contains("Main-Class")) { - task.manifest.attributes["Main-Class"] = mainClassName.get() - } - } - } - } - protected open fun Project.addRunTask() { val extension = applicationExtension tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) { task -> @@ -50,15 +42,15 @@ public abstract class ShadowApplicationPlugin : Plugin { i.destinationDir.resolve("lib/${s.archiveFile.get().asFile.name}") } task.classpath(jarFile) + task.mainModule.set(extension.mainModule) task.mainClass.set(extension.mainClass) - task.conventionMapping.map("jvmArgs", extension::getApplicationDefaultJvmArgs) - task.javaLauncher.set(javaToolchainService.launcherFor(javaPluginExtension.toolchain)) + task.jvmArguments.convention(provider { extension.applicationDefaultJvmArgs }) + + task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) + task.javaLauncher.convention(javaToolchainService.launcherFor(javaPluginExtension.toolchain)) } } - /** - * Syncs with [ApplicationPlugin.addCreateScriptsTask](https://github.com/gradle/gradle/blob/bcecbb416f19438c7532e309456e3c3ed287f8f5/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java#L184-L203). - */ protected open fun Project.addCreateScriptsTask() { val extension = applicationExtension tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { task -> @@ -104,30 +96,49 @@ public abstract class ShadowApplicationPlugin : Plugin { task.doLast { task.eachFile { if (it.path == "bin/${applicationName.get()}") { - it.permissions { permissions -> permissions.unix(755) } + it.permissions { permissions -> permissions.unix(UNIX_SCRIPT_PERMISSIONS) } } } } } } - protected open fun Project.configureDistSpec() { + protected open fun Project.configureDistribution() { distributions.register(ShadowBasePlugin.DISTRIBUTION_NAME) { distributions -> - distributions.contents { contents -> - contents.from(file("src/dist")) - contents.into("lib") { lib -> + distributions.contents { distSpec -> + distSpec.from(file("src/dist")) + distSpec.into("lib") { lib -> lib.from(tasks.shadowJar) lib.from(configurations.shadow) } - contents.into("bin") { bin -> + distSpec.into("bin") { bin -> bin.from(tasks.startShadowScripts) - bin.filePermissions { it.unix(493) } + bin.filePermissions { it.unix(UNIX_SCRIPT_PERMISSIONS) } + } + distSpec.with(applicationExtension.applicationDistribution) + } + } + } + + protected open fun Project.configureShadowJarMainClass() { + val mainClassName = applicationExtension.mainClass + tasks.shadowJar.configure { task -> + task.inputs.property("mainClassName", mainClassName) + task.doFirst { + // Inject the Main-Class attribute if it is not already present. + if (!task.manifest.attributes.contains("Main-Class")) { + task.manifest.attributes["Main-Class"] = mainClassName.get() } } } } public companion object { + /** + * Reflects the number of 755. + */ + private const val UNIX_SCRIPT_PERMISSIONS = "rwxr-xr-x" + public const val SHADOW_RUN_TASK_NAME: String = "runShadow" public const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" public const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" From a34d3c342dbcfcf5ebd64a8cd38f37f8e3fb8f99 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 5 Feb 2025 03:50:35 -0500 Subject: [PATCH 220/941] Remove the workaround for Gradle issue 22765 (#1225) Seems it's outdated... --- build.gradle.kts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0feaab7ba..19c32a56a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -89,19 +89,6 @@ testing.suites { testTask { // Required to enable `IssueExtension` for all tests. systemProperty("junit.jupiter.extensions.autodetection.enabled", true) - - // Required to test configuration cache in tests when using withDebug() - // https://github.com/gradle/gradle/issues/22765#issuecomment-1339427241 - jvmArgs( - "--add-opens", - "java.base/java.util=ALL-UNNAMED", - "--add-opens", - "java.base/java.util.concurrent.atomic=ALL-UNNAMED", - "--add-opens", - "java.base/java.lang.invoke=ALL-UNNAMED", - "--add-opens", - "java.base/java.net=ALL-UNNAMED", - ) } } dependencies { From b7b6593618e0e9c7b24b140d00e78cea2a34cff7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 5 Feb 2025 05:10:39 -0500 Subject: [PATCH 221/941] Mark RelocatorRemapper as internal (#1227) --- api/shadow.api | 9 --------- src/docs/changes/README.md | 4 ++++ .../shadow/{impl => internal}/RelocatorRemapper.kt | 10 +++++----- .../gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 2 +- 4 files changed, 10 insertions(+), 15 deletions(-) rename src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/{impl => internal}/RelocatorRemapper.kt (88%) diff --git a/api/shadow.api b/api/shadow.api index 4ff35dff6..631634e64 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -90,15 +90,6 @@ public class com/github/jengelman/gradle/plugins/shadow/ShadowStats { public fun toString ()Ljava/lang/String; } -public class com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper : org/objectweb/asm/commons/Remapper { - public fun (Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V - public fun hasRelocators ()Z - public fun map (Ljava/lang/String;)Ljava/lang/String; - public fun mapPath (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath;)Ljava/lang/String; - public fun mapPath (Ljava/lang/String;)Ljava/lang/String; - public fun mapValue (Ljava/lang/Object;)Ljava/lang/Object; -} - public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter : java/io/Serializable { public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index cfe199e41..b606b8233 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -7,6 +7,10 @@ - Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) +**Changed** + +- **BREAKING CHANGE:** Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) + ## [v9.0.0-beta7] (2025-02-02) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt similarity index 88% rename from src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 9337a3643..6fbba312f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/impl/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -1,4 +1,4 @@ -package com.github.jengelman.gradle.plugins.shadow.impl +package com.github.jengelman.gradle.plugins.shadow.internal import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext @@ -14,13 +14,13 @@ import org.objectweb.asm.commons.Remapper * * @author John Engelman */ -public open class RelocatorRemapper( +internal class RelocatorRemapper( private val relocators: Set, private val stats: ShadowStats, ) : Remapper() { private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") - public open fun hasRelocators(): Boolean = relocators.isNotEmpty() + fun hasRelocators(): Boolean = relocators.isNotEmpty() override fun mapValue(value: Any): Any { return if (value is String) { @@ -55,11 +55,11 @@ public open class RelocatorRemapper( return name } - public open fun mapPath(path: String): String { + fun mapPath(path: String): String { return map(path.substring(0, path.indexOf('.'))) } - public open fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String { + fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String { return mapPath(path.pathString) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index f9eb496b4..1c9498e1e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.impl.RelocatorRemapper +import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement From 51c0ae55b3d869e7512d8ee54b2176c1ad2d87d7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 5 Feb 2025 21:35:27 -0500 Subject: [PATCH 222/941] No need to call builtin builders (#1228) --- .../plugins/shadow/internal/RelocatorRemapper.kt | 4 ++-- .../plugins/shadow/tasks/ShadowCopyAction.kt | 12 ++++++------ .../transformers/ServiceFileTransformer.kt | 4 ++-- .../ApacheNoticeResourceTransformerTest.kt | 8 ++++---- .../ComponentsXmlResourceTransformerTest.kt | 16 ++++++++-------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 6fbba312f..4e8f59afd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -44,10 +44,10 @@ internal class RelocatorRemapper( for (relocator in relocators) { if (relocator.canRelocateClass(newName)) { - val classContext = RelocateClassContext.builder().className(newName).stats(stats).build() + val classContext = RelocateClassContext(className = newName, stats = stats) return prefix + relocator.relocateClass(classContext) + suffix } else if (relocator.canRelocatePath(newName)) { - val pathContext = RelocatePathContext.builder().path(newName).stats(stats).build() + val pathContext = RelocatePathContext(path = newName, stats = stats) return prefix + relocator.relocatePath(pathContext) + suffix } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 1c9498e1e..5433e7588 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -374,12 +374,12 @@ public open class ShadowCopyAction internal constructor( transformers.find { it.canTransformResource(element) }?.transform( - TransformerContext.builder() - .path(mappedPath) - .inputStream(steam) - .relocators(relocators) - .stats(stats) - .build(), + TransformerContext( + path = mappedPath, + inputStream = steam, + relocators = relocators, + stats = stats, + ), ) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index d9476b6d6..f81445719 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -48,12 +48,12 @@ public open class ServiceFileTransformer( var targetPath = context.path context.relocators.forEach { rel -> if (rel.canRelocateClass(File(targetPath).name)) { - val classContext = RelocateClassContext.builder().className(targetPath).stats(context.stats).build() + val classContext = RelocateClassContext(className = targetPath, stats = context.stats) targetPath = rel.relocateClass(classContext) } lines.forEachIndexed { i, line -> if (rel.canRelocateClass(line)) { - val lineContext = RelocateClassContext.builder().className(line).stats(context.stats).build() + val lineContext = RelocateClassContext(className = line, stats = context.stats) lines[i] = rel.relocateClass(lineContext) } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt index a8146761b..40ea4493d 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -63,10 +63,10 @@ class ApacheNoticeResourceTransformerTest : BaseTransformerTest Date: Thu, 6 Feb 2025 11:00:42 +0800 Subject: [PATCH 223/941] Revert "Remove the workaround for Gradle issue 22765 (#1225)" This reverts commit a34d3c342dbcfcf5ebd64a8cd38f37f8e3fb8f99. --- build.gradle.kts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 19c32a56a..0feaab7ba 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -89,6 +89,19 @@ testing.suites { testTask { // Required to enable `IssueExtension` for all tests. systemProperty("junit.jupiter.extensions.autodetection.enabled", true) + + // Required to test configuration cache in tests when using withDebug() + // https://github.com/gradle/gradle/issues/22765#issuecomment-1339427241 + jvmArgs( + "--add-opens", + "java.base/java.util=ALL-UNNAMED", + "--add-opens", + "java.base/java.util.concurrent.atomic=ALL-UNNAMED", + "--add-opens", + "java.base/java.lang.invoke=ALL-UNNAMED", + "--add-opens", + "java.base/java.net=ALL-UNNAMED", + ) } } dependencies { From c74de646d14450e87ed2ddc86303d29e04642a1a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Feb 2025 00:29:38 -0500 Subject: [PATCH 224/941] Simplify caching test assertions (#1229) * Simplify TransformerCachingTest * Add assertExecutionStates * Simplify MinimizationCachingTest * Simplify RelocationCachingTest * Simplify ShadowJarCachingTest * Rename assertExecutionStates to assertCompositeExecutions * Cleanups * Replace some `isRegular` assertions * Revert "Replace some `isRegular` assertions" This reverts commit 5ffb719c29adfd97da02b6a43c07eee4f4e95c98. --- .../shadow/caching/ApplicationCachingTest.kt | 2 + .../plugins/shadow/caching/BaseCachingTest.kt | 17 +++++ .../shadow/caching/MinimizationCachingTest.kt | 28 +++----- .../shadow/caching/RelocationCachingTest.kt | 28 +++----- .../shadow/caching/ShadowJarCachingTest.kt | 70 +++++++------------ .../shadow/caching/TransformerCachingTest.kt | 37 ++-------- 6 files changed, 69 insertions(+), 113 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt index 51609691d..9f17afede 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt @@ -37,6 +37,7 @@ class ApplicationCachingTest : BaseCachingTest() { } """.trimIndent(), ) + assertions("resource 1") resourcePath.writeText( @@ -44,6 +45,7 @@ class ApplicationCachingTest : BaseCachingTest() { Hello, World! %s from resource 2 """.trimIndent(), ) + assertions("resource 2") } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt index 2be19e5cc..c6bce26f1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt @@ -1,11 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow.caching +import assertk.Assert import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEmpty import assertk.assertions.isInstanceOf import com.github.jengelman.gradle.plugins.shadow.BasePluginTest +import com.github.jengelman.gradle.plugins.shadow.util.JarPath import java.nio.file.NoSuchFileException import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.isDirectory @@ -42,6 +44,21 @@ abstract class BaseCachingTest : BasePluginTest() { assertRunWithResult(UP_TO_DATE, outputs = outputs) } + /** + * Combines [assertExecutionSuccess] and [assertExecutionsFromCacheAndUpToDate] for simplifying assertions. + */ + fun assertCompositeExecutions( + vararg outputs: String, + jarPathProvider: () -> JarPath = { outputShadowJar }, + jarPathAssertions: Assert.() -> Unit = {}, + ) { + // First run should execute. + assertExecutionSuccess() + assertThat(jarPathProvider()).useAll(jarPathAssertions) + // Subsequent runs should be from cache and up-to-date after configurations changed. + assertExecutionsFromCacheAndUpToDate(outputs = outputs) + } + private fun assertRunWithResult(expectedOutcome: TaskOutcome, vararg outputs: String) { val result = run(taskPath) assertThat(result).taskOutcomeEquals(taskPath, expectedOutcome) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt index 7c9f9ee8d..70eed34d0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.caching -import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries @@ -22,8 +21,7 @@ class MinimizationCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertExecutionSuccess() - assertThat(outputShadowJar).useAll { + assertCompositeExecutions { containsEntries( "server/Server.class", "junit/framework/Test.class", @@ -40,21 +38,15 @@ class MinimizationCachingTest : BaseCachingTest() { } """.trimIndent(), ) - val assertions = { - assertThat(outputShadowJar).useAll { - containsEntries( - "server/Server.class", - "junit/framework/Test.class", - ) - doesNotContainEntries( - "client/Client.class", - ) - } - } - assertExecutionSuccess() - assertions() - assertExecutionsFromCacheAndUpToDate() - assertions() + assertCompositeExecutions { + containsEntries( + "server/Server.class", + "junit/framework/Test.class", + ) + doesNotContainEntries( + "client/Client.class", + ) + } } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index aa440f298..3204dda47 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.caching -import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import kotlin.io.path.appendText @@ -21,8 +20,7 @@ class RelocationCachingTest : BaseCachingTest() { ) writeMainClass(withImports = true) - assertExecutionSuccess() - assertThat(outputShadowJar).useAll { + assertCompositeExecutions { containsEntries( "shadow/Main.class", "junit/framework/Test.class", @@ -36,21 +34,15 @@ class RelocationCachingTest : BaseCachingTest() { } """.trimIndent(), ) - val assertions = { - assertThat(outputShadowJar).useAll { - containsEntries( - "shadow/Main.class", - "foo/junit/framework/Test.class", - ) - doesNotContainEntries( - "junit/framework/Test.class", - ) - } - } - assertExecutionSuccess() - assertions() - assertExecutionsFromCacheAndUpToDate() - assertions() + assertCompositeExecutions { + containsEntries( + "shadow/Main.class", + "foo/junit/framework/Test.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 80facbb04..d350e24ce 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -25,16 +25,18 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertExecutionSuccess() - assertExecutionsFromCacheAndUpToDate() + assertCompositeExecutions { + isRegular() + } val replaced = projectScriptPath.readText().lines() .filterNot { it == fromJar(projectJar) } .joinToString(System.lineSeparator()) projectScriptPath.writeText(replaced) - assertExecutionSuccess() - assertExecutionsFromCacheAndUpToDate() + assertCompositeExecutions { + isRegular() + } } @Test @@ -47,8 +49,9 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) - assertExecutionSuccess() - assertThat(outputShadowJar).isRegular() + assertCompositeExecutions { + isRegular() + } projectScriptPath.appendText( """ @@ -78,9 +81,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) - // First run successful with all files. - assertExecutionSuccess() - assertThat(outputShadowJar).useAll { + assertCompositeExecutions { containsEntries( "shadow/Main.class", "shadow/Main2.class", @@ -97,9 +98,8 @@ class ShadowJarCachingTest : BaseCachingTest() { } """.trimIndent() + System.lineSeparator(), ) - // Second run successful after excludes changed. - assertExecutionSuccess() - assertThat(outputShadowJar).useAll { + + assertCompositeExecutions { containsEntries( "shadow/Main.class", "shadow/Main2.class", @@ -118,9 +118,8 @@ class ShadowJarCachingTest : BaseCachingTest() { } """.trimIndent() + System.lineSeparator(), ) - // Third run successful after includes changed. - assertExecutionSuccess() - assertThat(outputShadowJar).useAll { + + assertCompositeExecutions { containsEntries( "shadow/Main.class", ) @@ -139,9 +138,8 @@ class ShadowJarCachingTest : BaseCachingTest() { } """.trimIndent() + System.lineSeparator(), ) - // Forth run successful after includes changed again. - assertExecutionSuccess() - assertThat(outputShadowJar).useAll { + + assertCompositeExecutions { containsEntries( "shadow/Main.class", "shadow/Main2.class", @@ -152,9 +150,6 @@ class ShadowJarCachingTest : BaseCachingTest() { "b.properties", ) } - - // Clean and run 2 more times to ensure the states are cached and up-to-date. - assertExecutionsFromCacheAndUpToDate() } @Test @@ -168,8 +163,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) - assertExecutionSuccess() - assertThat(outputShadowJar).useAll { + assertCompositeExecutions { containsEntries( "shadow/Main.class", "junit/framework/Test.class", @@ -185,21 +179,15 @@ class ShadowJarCachingTest : BaseCachingTest() { } """.trimIndent(), ) - val assertions = { - assertThat(outputShadowJar).useAll { - containsEntries( - "shadow/Main.class", - ) - doesNotContainEntries( - "junit/framework/Test.class", - ) - } - } - assertExecutionSuccess() - assertions() - assertExecutionsFromCacheAndUpToDate() - assertions() + assertCompositeExecutions { + containsEntries( + "shadow/Main.class", + ) + doesNotContainEntries( + "junit/framework/Test.class", + ) + } } @Test @@ -213,7 +201,7 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent() + System.lineSeparator(), ) val assertions = { - assertThat(outputShadowJar).useAll { + assertCompositeExecutions { containsEntries( "c.properties", "d.properties", @@ -221,9 +209,6 @@ class ShadowJarCachingTest : BaseCachingTest() { } } - assertExecutionSuccess() - assertions() - assertExecutionsFromCacheAndUpToDate() assertions() projectScriptPath.appendText( @@ -234,9 +219,6 @@ class ShadowJarCachingTest : BaseCachingTest() { """.trimIndent(), ) - assertExecutionSuccess() - assertions() - assertExecutionsFromCacheAndUpToDate() assertions() } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index e5468842e..2d11b0d53 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.caching -import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.transformers.ApacheLicenseResourceTransformer @@ -38,12 +37,11 @@ class TransformerCachingTest : BaseCachingTest() { @Test fun shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer() { val assertions = { - assertThat(outputShadowJar).useAll { + assertCompositeExecutions { containsEntries("shadow/Main.class") } } - assertExecutionSuccess() assertions() projectScriptPath.appendText( @@ -53,19 +51,12 @@ class TransformerCachingTest : BaseCachingTest() { """.trimIndent(), ), ) - assertExecutionSuccess() - assertions() - assertExecutionsFromCacheAndUpToDate() assertions() val replaced = projectScriptPath.readText().replace("META-INF/foo", "META-INF/bar") projectScriptPath.writeText(replaced) - assertExecutionSuccess() - assertions() - - assertExecutionsFromCacheAndUpToDate() assertions() } @@ -73,13 +64,12 @@ class TransformerCachingTest : BaseCachingTest() { fun shadowJarIsCachedCorrectlyWhenUsingAppendingTransformer() { path("src/main/resources/foo/bar.properties").writeText("foo=bar") val assertions = { name: String -> - assertThat(outputShadowJar).useAll { + assertCompositeExecutions { containsEntries("shadow/Main.class", "foo/$name.properties") getContent("foo/$name.properties").isEqualTo("foo=$name") } } - assertExecutionSuccess() assertions("bar") projectScriptPath.appendText( @@ -89,10 +79,7 @@ class TransformerCachingTest : BaseCachingTest() { """.trimIndent(), ), ) - assertExecutionSuccess() - assertions("bar") - assertExecutionsFromCacheAndUpToDate() assertions("bar") path("src/main/resources/foo/bar.properties").deleteExisting() @@ -100,10 +87,6 @@ class TransformerCachingTest : BaseCachingTest() { val replaced = projectScriptPath.readText().replace("foo/bar.properties", "foo/baz.properties") projectScriptPath.writeText(replaced) - assertExecutionSuccess() - assertions("baz") - - assertExecutionsFromCacheAndUpToDate() assertions("baz") } @@ -111,13 +94,12 @@ class TransformerCachingTest : BaseCachingTest() { fun shadowJarIsCachedCorrectlyWhenUsingXmlAppendingTransformer() { path("src/main/resources/foo/bar.xml").writeText("bar") val assertions = { name: String -> - assertThat(outputShadowJar).useAll { + assertCompositeExecutions { containsEntries("shadow/Main.class", "foo/$name.xml") getContent("foo/$name.xml").contains("$name") } } - assertExecutionSuccess() assertions("bar") projectScriptPath.appendText( @@ -127,10 +109,7 @@ class TransformerCachingTest : BaseCachingTest() { """.trimIndent(), ), ) - assertExecutionSuccess() - assertions("bar") - assertExecutionsFromCacheAndUpToDate() assertions("bar") path("src/main/resources/foo/bar.xml").deleteExisting() @@ -138,10 +117,6 @@ class TransformerCachingTest : BaseCachingTest() { val replaced = projectScriptPath.readText().replace("foo/bar.xml", "foo/baz.xml") projectScriptPath.writeText(replaced) - assertExecutionSuccess() - assertions("baz") - - assertExecutionsFromCacheAndUpToDate() assertions("baz") } @@ -153,12 +128,11 @@ class TransformerCachingTest : BaseCachingTest() { path("test/some.file").writeText("some content") } val assertions = { - assertThat(outputShadowJar).useAll { + assertCompositeExecutions { containsEntries("shadow/Main.class") } } - assertExecutionSuccess() assertions() projectScriptPath.appendText( @@ -168,10 +142,7 @@ class TransformerCachingTest : BaseCachingTest() { } """.trimIndent(), ) - assertExecutionSuccess() - assertions() - assertExecutionsFromCacheAndUpToDate() assertions() } From 6b570f5bc0e20c632160a2b73d7ad029c4a2111d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Feb 2025 01:36:33 -0500 Subject: [PATCH 225/941] Revert "Try out ubuntu-24.04-arm on CI (#1164)" (#1230) This reverts commit 618cd01e3c07b8005fbf8717335e1abded8e03b3. --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38b7e36f3..4c393c30b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - os: [ ubuntu-24.04-arm, windows-latest ] + os: [ ubuntu-latest, windows-latest ] # Always test on the latest version and some LTS. java: [ 17, 21, 23 ] runs-on: ${{ matrix.os }} @@ -26,7 +26,7 @@ jobs: publish-snapshot: needs: build - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-latest if: github.repository == 'GradleUp/shadow' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ef4b7ca1..a3e9d3ca1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-latest if: github.repository == 'GradleUp/shadow' permissions: contents: write From 479e5f9d75b36f3c73146f2966be11b1c642cfab Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Feb 2025 01:54:12 -0500 Subject: [PATCH 226/941] Simplify ServiceFileTransformer and remove ServiceStream (#1218) * Copy ServicesResourceTransformer.java from https://github.com/apache/maven-shade-plugin/blob/49f6e13de5748700ef2185795f89f66be61554ef/src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java * Use nio Path * Convert ServicesResourceTransformer to Kotlin * Merge ServicesResourceTransformer into ServiceFileTransformer * Fix merge * Fix tests * Cleanups * Update changelog * Note path * Use substringAfter * Better error message in ZipFile.getStream * Add canTransformAlternateResource * Copy ServiceResourceTransformerTest.java from https://github.com/apache/maven-shade-plugin/blob/49f6e13de5748700ef2185795f89f66be61554ef/src/test/java/org/apache/maven/plugins/shade/resource/ServiceResourceTransformerTest.java * Convert ServiceResourceTransformerTest to Kotlin * Merge ServiceResourceTransformerTest into ServiceFileTransformerTest * Use nio Path in tests * Cleanup * Kdoc for ServiceFileTransformerTest * Close zip files --- api/shadow.api | 9 +- src/docs/changes/README.md | 4 + .../gradle/plugins/shadow/util/JarPath.kt | 2 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 2 +- .../transformers/ServiceFileTransformer.kt | 77 ++++------- .../transformers/BaseTransformerTest.kt | 2 +- .../ServiceFileTransformerTest.kt | 130 +++++++++++++++++- 7 files changed, 166 insertions(+), 60 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 631634e64..8d93b1465 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -547,6 +547,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFile public fun exclude ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; public fun getExcludes ()Ljava/util/Set; public fun getIncludes ()Ljava/util/Set; + public fun getPath ()Ljava/lang/String; public fun hasTransformedResource ()Z public fun include (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; public fun include (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; @@ -555,16 +556,10 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFile public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V public fun setExcludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; public fun setIncludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; - public fun setPath (Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun setPath (Ljava/lang/String;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer$ServiceStream : java/io/ByteArrayOutputStream { - public fun ()V - public fun append (Ljava/io/InputStream;)V - public fun toInputStream ()Ljava/io/InputStream; -} - public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer : org/gradle/api/Named { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer$Companion; public abstract fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index b606b8233..ac1d26d49 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -11,6 +11,10 @@ - **BREAKING CHANGE:** Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) +**Removed** + +- **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) + ## [v9.0.0-beta7] (2025-02-02) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index ec66a9136..025bcc64b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -29,7 +29,7 @@ fun ZipFile.getContent(entryName: String): String { } fun ZipFile.getStream(entryName: String): InputStream { - val entry = getEntry(entryName) ?: error("Entry not found: $entryName") + val entry = getEntry(entryName) ?: error("Entry $entryName not found in all entries: ${entries().toList()}") return getInputStream(entry) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 4f1edc074..9eeb95fc5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -196,7 +196,7 @@ public abstract class ShadowJar : override fun mergeServiceFiles(rootPath: String): ShadowJar { return runCatching { transform(ServiceFileTransformer::class.java) { - it.setPath(rootPath) + it.path = rootPath } }.getOrDefault(this) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index f81445719..cf384f2a4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -2,11 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction -import java.io.ByteArrayInputStream -import java.io.ByteArrayOutputStream -import java.io.File -import java.io.IOException -import java.io.InputStream +import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement @@ -32,11 +28,18 @@ import org.gradle.api.tasks.util.PatternSet public open class ServiceFileTransformer( private val patternSet: PatternSet = PatternSet() .include(SERVICES_PATTERN) - .exclude(GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN), + .exclude(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), ) : Transformer, PatternFilterable by patternSet { @get:Internal - internal val serviceEntries = mutableMapOf() + internal val serviceEntries = mutableMapOf>() + + @get:Internal // No need to mark this as an input as `getIncludes` is already marked as `@Input`. + public open var path: String = SERVICES_PATH + set(value) { + field = value + patternSet.setIncludes(listOf("$value/**")) + } override fun canTransformResource(element: FileTreeElement): Boolean { val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element @@ -44,37 +47,37 @@ public open class ServiceFileTransformer( } override fun transform(context: TransformerContext) { - val lines = context.inputStream.bufferedReader().readLines().toMutableList() - var targetPath = context.path - context.relocators.forEach { rel -> - if (rel.canRelocateClass(File(targetPath).name)) { - val classContext = RelocateClassContext(className = targetPath, stats = context.stats) - targetPath = rel.relocateClass(classContext) + var resource = context.path.substringAfter("$path/") + context.relocators.forEach { relocator -> + if (relocator.canRelocateClass(resource)) { + val classContext = RelocateClassContext(className = resource, stats = context.stats) + resource = relocator.relocateClass(classContext) + return@forEach } - lines.forEachIndexed { i, line -> - if (rel.canRelocateClass(line)) { + } + resource = "$path/$resource" + val out = serviceEntries.getOrPut(resource) { mutableSetOf() } + + context.inputStream.bufferedReader().use { it.readLines() }.forEach { + var line = it + context.relocators.forEach { relocator -> + if (relocator.canRelocateClass(line)) { val lineContext = RelocateClassContext(className = line, stats = context.stats) - lines[i] = rel.relocateClass(lineContext) + line = relocator.relocateClass(lineContext) } } - } - lines.forEach { line -> - serviceEntries[targetPath] = serviceEntries.getOrDefault(targetPath, ServiceStream()).apply { - append(ByteArrayInputStream(line.toByteArray())) - } + out.add(line) } } override fun hasTransformedResource(): Boolean = serviceEntries.isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - serviceEntries.forEach { (path, stream) -> + serviceEntries.forEach { (path, data) -> val entry = ZipEntry(path) entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) - stream.toInputStream().use { - it.copyTo(os) - } + os.write(data.joinToString("\n").toByteArray()) os.closeEntry() } } @@ -85,28 +88,8 @@ public open class ServiceFileTransformer( @Input // TODO: https://github.com/GradleUp/shadow/issues/1202 override fun getExcludes(): MutableSet = patternSet.excludes - public open fun setPath(path: String): PatternFilterable = apply { - patternSet.setIncludes(listOf("$path/**")) - } - - public open class ServiceStream : ByteArrayOutputStream(1024) { - @Throws(IOException::class) - public open fun append(inputStream: InputStream) { - if (count > 0 && buf[count - 1] != '\n'.code.toByte() && buf[count - 1] != '\r'.code.toByte()) { - val newline = "\n".toByteArray() - write(newline, 0, newline.size) - } - inputStream.use { - it.copyTo(this) - } - } - - public open fun toInputStream(): InputStream = ByteArrayInputStream(buf, 0, count) - } - private companion object { - private const val SERVICES_PATTERN = "META-INF/services/**" - private const val GROOVY_EXTENSION_MODULE_DESCRIPTOR_PATTERN = - "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" + private const val SERVICES_PATH = "META-INF/services" + private const val SERVICES_PATTERN = "$SERVICES_PATH/**" } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 6e94fcf97..3a963a537 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -23,7 +23,7 @@ abstract class BaseTransformerTest { get() = TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME)) @BeforeEach - fun setup() { + open fun setup() { @Suppress("UNCHECKED_CAST") val clazz = (this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments.first() as Class transformer = clazz.create(testObjectFactory) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index c2841d399..8450aeb09 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -4,12 +4,40 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.util.SimpleRelocator +import java.io.InputStream +import java.io.OutputStream +import java.nio.file.Path +import java.util.zip.ZipFile +import kotlin.io.path.createTempFile +import kotlin.io.path.deleteExisting +import kotlin.io.path.outputStream +import org.apache.tools.zip.ZipOutputStream +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource +/** + * Modified from [org.apache.maven.plugins.shade.resource.ServiceResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ServiceResourceTransformerTest.java). + */ class ServiceFileTransformerTest : BaseTransformerTest() { + private lateinit var tempJar: Path + + @BeforeEach + override fun setup() { + super.setup() + tempJar = createTempFile("shade.", ".jar") + } + + @AfterEach + fun cleanup() { + tempJar.deleteExisting() + } + @ParameterizedTest @MethodSource("resourceProvider") fun canTransformResource(path: String, exclude: Boolean, expected: Boolean) { @@ -28,7 +56,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() } assertThat(transformer.hasTransformedResource()).isTrue() - val entry = transformer.serviceEntries.getValue(path).toInputStream().bufferedReader().use { it.readText() } + val entry = transformer.serviceEntries.getValue(path).joinToString("\n") assertThat(entry).isEqualTo(output) } @@ -38,9 +66,105 @@ class ServiceFileTransformerTest : BaseTransformerTest() assertThat(transformer.canTransformResource(element)).isFalse() } + @Test + fun canTransformAlternateResource() { + transformer.path = "foo/bar" + assertThat(transformer.canTransformResource("foo/bar/moo/goo/Zoo")).isTrue() + assertThat(transformer.canTransformResource("META-INF/services/Zoo")).isFalse() + } + + @Test + fun relocatedClasses() { + val relocator = SimpleRelocator("org.foo", "borg.foo", excludes = listOf("org.foo.exclude.*")) + val content = "org.foo.Service\norg.foo.exclude.OtherService\n" + val contentResource = "META-INF/services/org.foo.something.another" + val contentResourceShaded = "META-INF/services/borg.foo.something.another" + + val transformer = ServiceFileTransformer() + transformer.transform(context(contentResource, content, relocator)) + + tempJar.outputStream().zipOutputStream().use { zos -> + transformer.modifyOutputStream(zos, false) + } + + val transformedContent = ZipFile(tempJar.toFile()).use { it.getContent(contentResourceShaded) } + assertThat(transformedContent).isEqualTo("borg.foo.Service\norg.foo.exclude.OtherService") + } + + @Test + fun mergeRelocatedFiles() { + val relocator = SimpleRelocator("org.foo", "borg.foo", excludes = listOf("org.foo.exclude.*")) + val content = "org.foo.Service\norg.foo.exclude.OtherService\n" + val contentResource = "META-INF/services/org.foo.something.another" + val contentResourceShaded = "META-INF/services/borg.foo.something.another" + + val transformer = ServiceFileTransformer() + transformer.transform(context(contentResource, content, relocator)) + transformer.transform(context(contentResourceShaded, content, relocator)) + + tempJar.outputStream().zipOutputStream().use { zos -> + transformer.modifyOutputStream(zos, false) + } + + val transformedContent = ZipFile(tempJar.toFile()).use { it.getContent(contentResourceShaded) } + assertThat(transformedContent).isEqualTo("borg.foo.Service\norg.foo.exclude.OtherService") + } + + @Test + fun concatenationAppliedMultipleTimes() { + val relocator = SimpleRelocator("org.eclipse", "org.eclipse1234") + val content = "org.eclipse.osgi.launch.EquinoxFactory\n" + val contentResource = "META-INF/services/org.osgi.framework.launch.FrameworkFactory" + + val transformer = ServiceFileTransformer() + transformer.transform(context(contentResource, content, relocator)) + + tempJar.outputStream().zipOutputStream().use { zos -> + transformer.modifyOutputStream(zos, false) + } + + val transformedContent = ZipFile(tempJar.toFile()).use { it.getContent(contentResource) } + assertThat(transformedContent).isEqualTo("org.eclipse1234.osgi.launch.EquinoxFactory") + } + + @Test + fun concatenation() { + val relocator = SimpleRelocator("org.foo", "borg.foo") + var content = "org.foo.Service\n" + var contentResource = "META-INF/services/org.something.another" + + val transformer = ServiceFileTransformer() + transformer.transform(context(contentResource, content, relocator)) + + content = "org.blah.Service\n" + contentResource = "META-INF/services/org.something.another" + + transformer.transform(context(contentResource, content, relocator)) + + tempJar.outputStream().zipOutputStream().use { zos -> + transformer.modifyOutputStream(zos, false) + } + + val transformedContent = ZipFile(tempJar.toFile()).use { it.getContent(contentResource) } + assertThat(transformedContent).isEqualTo("borg.foo.Service\norg.blah.Service") + } + private companion object { - fun context(path: String, input: String): TransformerContext { - return TransformerContext(path, input.byteInputStream(), stats = sharedStats) + fun context(path: String, input: String, vararg relocators: Relocator): TransformerContext { + return TransformerContext(path, input.byteInputStream(), relocators = relocators.toSet(), stats = sharedStats) + } + + fun OutputStream.zipOutputStream(): ZipOutputStream { + return if (this is ZipOutputStream) this else ZipOutputStream(this) + } + + fun ZipFile.getContent(entryName: String): String { + return getStream(entryName).bufferedReader().use { it.readText() } + } + + fun ZipFile.getStream(entryName: String): InputStream { + val entry = getEntry(entryName) ?: error("Entry $entryName not found in all entries: ${entries().toList()}") + return getInputStream(entry) } @JvmStatic From 99521e8f8ce908cb3b11046b1e30f786c6e97c4b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Feb 2025 03:20:55 -0500 Subject: [PATCH 227/941] Unify test jar res and entry checks (#1231) * Remove `shadowCopy` * Replace artifactJar and projectJar * Replace `isRegular` * Add artifactAEntries, artifactBEntries, and artifactABEntries * Fix tests * Replace more entries * Tweak `canUseCustomTransformer`, `otherTransformers`, and `supportZipCompressionStored` * Cleanups * Better error message for `containsEntries` and `doesNotContainEntries` * Fix `supportZipCompressionStored` --- .../plugins/shadow/ApplicationPluginTest.kt | 4 +- .../gradle/plugins/shadow/BasePluginTest.kt | 18 ++++-- .../gradle/plugins/shadow/FilteringTest.kt | 30 +++------- .../gradle/plugins/shadow/JavaPluginTest.kt | 55 +++++------------- .../gradle/plugins/shadow/PublishingTest.kt | 22 ++----- .../shadow/caching/ShadowJarCachingTest.kt | 39 +++++-------- .../shadow/transformers/TransformersTest.kt | 19 ++++-- .../gradle/plugins/shadow/util/JarPath.kt | 16 +---- .../resources/test-artifact-1.0-SNAPSHOT.jar | Bin 3115 -> 0 bytes .../resources/test-project-1.0-SNAPSHOT.jar | Bin 3906 -> 0 bytes 10 files changed, 77 insertions(+), 126 deletions(-) delete mode 100644 src/functionalTest/resources/test-artifact-1.0-SNAPSHOT.jar delete mode 100644 src/functionalTest/resources/test-project-1.0-SNAPSHOT.jar diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index a4e00e9b3..957beda59 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -133,7 +133,7 @@ class ApplicationPluginTest : BasePluginTest() { assertions(run(runShadowTask).output, "foo") commonAssertions( jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"), - entriesContained = arrayOf("a.properties", "a2.properties", "shadow/Main.class", "shadow/Main2.class"), + entriesContained = entriesInA + arrayOf("shadow/Main.class", "shadow/Main2.class"), mainClassAttr = "shadow.Main2", ) @@ -242,7 +242,7 @@ class ApplicationPluginTest : BasePluginTest() { private fun commonAssertions( jarPath: JarPath, - entriesContained: Array = arrayOf("a.properties", "a2.properties", "shadow/Main.class"), + entriesContained: Array = entriesInA + "shadow/Main.class", mainClassAttr: String = "shadow.Main", classPathAttr: String? = null, ) { 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 62fa1d6ba..a00f7f7f5 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 @@ -43,6 +43,12 @@ abstract class BasePluginTest { lateinit var projectRoot: Path lateinit var localRepo: AppendableMavenRepository + lateinit var artifactAJar: Path + lateinit var artifactBJar: Path + lateinit var entriesInA: Array + lateinit var entriesInB: Array + lateinit var entriesInAB: Array + @BeforeAll open fun doFirst() { localRepo = AppendableMavenRepository( @@ -50,7 +56,7 @@ abstract class BasePluginTest { runner(projectDir = null), ) localRepo.module("junit", "junit", "3.8.2") { - useJar(testJar) + useJar(junitJar) }.module("shadow", "a", "1.0") { buildJar { insert("a.properties", "a") @@ -61,6 +67,12 @@ abstract class BasePluginTest { insert("b.properties", "b") } }.publish() + + artifactAJar = path("shadow/a/1.0/a-1.0.jar", parent = localRepo.root) + artifactBJar = path("shadow/b/1.0/b-1.0.jar", parent = localRepo.root) + entriesInA = arrayOf("a.properties", "a2.properties") + entriesInB = arrayOf("b.properties") + entriesInAB = entriesInA + entriesInB } @BeforeEach @@ -333,9 +345,7 @@ abstract class BasePluginTest { Path(gradleUserHome, "testkit") } - val testJar: Path = requireResourceAsPath("junit-3.8.2.jar") - val artifactJar: Path = requireResourceAsPath("test-artifact-1.0-SNAPSHOT.jar") - val projectJar: Path = requireResourceAsPath("test-project-1.0-SNAPSHOT.jar") + val junitJar: Path = requireResourceAsPath("junit-3.8.2.jar") val shadowJar: String = """ tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name}) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 319d341f4..442511627 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -35,11 +35,7 @@ class FilteringTest : BasePluginTest() { fun includeAllDependencies() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( - "a.properties", - "a2.properties", - "b.properties", - ) + containsEntries(*entriesInAB) } } @@ -110,12 +106,8 @@ class FilteringTest : BasePluginTest() { assertThat(result).taskOutcomeEquals(shadowJarTask, SUCCESS) assertThat(outputShadowJar).useAll { - containsEntries( - "a.properties", - "a2.properties", - "b.properties", - "d.properties", - ) + val entries = entriesInAB + "d.properties" + containsEntries(*entries) doesNotContainEntries( "c.properties", ) @@ -178,12 +170,8 @@ class FilteringTest : BasePluginTest() { "d.properties", "shadow/Passed.class", ) - doesNotContainEntries( - "a.properties", - "a2.properties", - "b.properties", - "c.properties", - ) + val entries = entriesInAB + "c.properties" + doesNotContainEntries(*entries) } } @@ -285,12 +273,8 @@ class FilteringTest : BasePluginTest() { private fun commonAssertions() { assertThat(outputShadowJar).useAll { - containsEntries( - "a.properties", - "a2.properties", - "b.properties", - "c.properties", - ) + val entries = entriesInAB + "c.properties" + containsEntries(*entries) doesNotContainEntries( "d.properties", ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index e52d839dd..998318b56 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -17,7 +17,6 @@ import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr -import com.github.jengelman.gradle.plugins.shadow.util.isRegular import com.github.jengelman.gradle.plugins.shadow.util.runProcess import kotlin.io.path.appendText import kotlin.io.path.writeText @@ -64,6 +63,7 @@ class JavaPluginTest : BasePluginTest() { disabledReason = "Gradle 8.3 doesn't support Java 21.", ) fun compatibleWithMinGradleVersion() { + writeMainClass(withImports = true) projectScriptPath.appendText( """ dependencies { @@ -76,7 +76,12 @@ class JavaPluginTest : BasePluginTest() { it.withGradleVersion("8.3") } - assertThat(outputShadowJar).isRegular() + assertThat(outputShadowJar).useAll { + containsEntries( + "shadow/Main.class", + "junit/framework/Test.class", + ) + } } @Test @@ -86,21 +91,6 @@ class JavaPluginTest : BasePluginTest() { } } - @Test - fun shadowCopy() { - projectScriptPath.appendText( - """ - $shadowJar { - ${fromJar(artifactJar, projectJar)} - } - """.trimIndent(), - ) - - run(shadowJarTask) - - assertThat(outputShadowJar).isRegular() - } - @Test fun includeProjectSources() { path("src/main/java/shadow/Passed.java").writeText( @@ -265,13 +255,8 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( - "a.properties", - "a2.properties", - ) - doesNotContainEntries( - "b.properties", - ) + containsEntries(*entriesInA) + doesNotContainEntries(*entriesInB) } } @@ -333,13 +318,8 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( - "a.properties", - "a2.properties", - ) - doesNotContainEntries( - "b.properties", - ) + containsEntries(*entriesInA) + doesNotContainEntries(*entriesInB) } } @@ -448,7 +428,9 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(outputShadowJar).isRegular() + assertThat(outputShadowJar).useAll { + transform { it.entries().toList() }.isNotEmpty() + } } @Issue( @@ -476,13 +458,8 @@ class JavaPluginTest : BasePluginTest() { // Doesn't contain Gradle classes. getMainAttr("Class-Path").isNull() - containsEntries( - "a.properties", - "a2.properties", - ) - doesNotContainEntries( - "b.properties", - ) + containsEntries(*entriesInA) + doesNotContainEntries(*entriesInB) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 0b07b9800..0dcaedb5f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -243,12 +243,8 @@ class PublishingTest : BasePluginTest() { "aa.properties", "aa2.properties", ) - doesNotContainEntries( - "a.properties", - "a2.properties", - "b.properties", - "bb.properties", - ) + val entries = entriesInAB + "bb.properties" + doesNotContainEntries(*entries) } assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven-all/1.0/maven-all-1.0.module"))) @@ -331,12 +327,11 @@ class PublishingTest : BasePluginTest() { publish() - val entries = arrayOf("a.properties", "a2.properties", "b.properties") assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0.jar")).useAll { - doesNotContainEntries(*entries) + doesNotContainEntries(*entriesInAB) } assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0-all.jar")).useAll { - containsEntries(*entries) + containsEntries(*entriesInAB) } assertPomCommon(repoPath("com/acme/maven/1.0/maven-1.0.pom"), arrayOf("shadow:a:1.0", "shadow:b:1.0")) @@ -457,13 +452,8 @@ class PublishingTest : BasePluginTest() { private fun assertShadowJarCommon(jarPath: JarPath) { assertThat(jarPath).useAll { - containsEntries( - "a.properties", - "a2.properties", - ) - doesNotContainEntries( - "b.properties", - ) + containsEntries(*entriesInA) + doesNotContainEntries(*entriesInB) getMainAttr("Class-Path").isEqualTo("b-1.0.jar") } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index d350e24ce..4e982969f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -5,7 +5,6 @@ import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFil import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries -import com.github.jengelman.gradle.plugins.shadow.util.isRegular import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText @@ -20,22 +19,23 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - ${fromJar(artifactJar, projectJar)} + ${fromJar(artifactAJar, artifactBJar)} } """.trimIndent(), ) assertCompositeExecutions { - isRegular() + containsEntries(*entriesInAB) } val replaced = projectScriptPath.readText().lines() - .filterNot { it == fromJar(projectJar) } + .filterNot { it == fromJar(artifactBJar) } .joinToString(System.lineSeparator()) projectScriptPath.writeText(replaced) assertCompositeExecutions { - isRegular() + containsEntries(*entriesInA) + doesNotContainEntries(*entriesInB) } } @@ -44,13 +44,13 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - ${fromJar(artifactJar, projectJar)} + ${fromJar(artifactAJar, artifactBJar)} } """.trimIndent() + System.lineSeparator(), ) assertCompositeExecutions { - isRegular() + containsEntries(*entriesInAB) } projectScriptPath.appendText( @@ -62,7 +62,9 @@ class ShadowJarCachingTest : BaseCachingTest() { ) assertExecutionsFromCacheAndUpToDate() - assertThat(jarPath("build/libs/foo-1.0-all.jar")).isRegular() + assertThat(jarPath("build/libs/foo-1.0-all.jar")).useAll { + containsEntries(*entriesInAB) + } } @Issue( @@ -82,13 +84,8 @@ class ShadowJarCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries( - "shadow/Main.class", - "shadow/Main2.class", - "a.properties", - "a2.properties", - "b.properties", - ) + val entries = entriesInAB + arrayOf("shadow/Main.class", "shadow/Main2.class") + containsEntries(*entries) } projectScriptPath.appendText( @@ -104,11 +101,7 @@ class ShadowJarCachingTest : BaseCachingTest() { "shadow/Main.class", "shadow/Main2.class", ) - doesNotContainEntries( - "a.properties", - "a2.properties", - "b.properties", - ) + doesNotContainEntries(*entriesInAB) } projectScriptPath.appendText( @@ -144,11 +137,7 @@ class ShadowJarCachingTest : BaseCachingTest() { "shadow/Main.class", "shadow/Main2.class", ) - doesNotContainEntries( - "a.properties", - "a2.properties", - "b.properties", - ) + doesNotContainEntries(*entriesInAB) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 2f56d0183..561f8c86d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -8,8 +8,8 @@ import assertk.assertions.isNotNull import assertk.assertions.isNull import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.getStream -import com.github.jengelman.gradle.plugins.shadow.util.isRegular import java.util.jar.Attributes import kotlin.io.path.appendText import kotlin.io.path.writeText @@ -105,9 +105,12 @@ class TransformersTest : BaseTransformerTest() { @Test fun canUseCustomTransformer() { - writeMainClass() projectScriptPath.appendText( """ + dependencies { + implementation 'shadow:a:1.0' + implementation 'shadow:b:1.0' + } $shadowJar { // Use NoOpTransformer to mock a custom transformer here. transform(${NoOpTransformer::class.java.name}.INSTANCE) @@ -117,7 +120,9 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar).isRegular() + assertThat(outputShadowJar).useAll { + containsEntries(*entriesInAB) + } } @ParameterizedTest @@ -129,6 +134,10 @@ class TransformersTest : BaseTransformerTest() { } projectScriptPath.appendText( """ + dependencies { + implementation 'shadow:a:1.0' + implementation 'shadow:b:1.0' + } $shadowJar { transform(${transformer.java.name}) $configuration } @@ -137,7 +146,9 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) - assertThat(outputShadowJar).isRegular() + assertThat(outputShadowJar).useAll { + containsEntries(*entriesInAB) + } } private fun commonAssertions( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index 025bcc64b..bf79c670a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -1,8 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.util import assertk.Assert -import assertk.all -import assertk.assertions.isNotEmpty import assertk.fail import java.io.InputStream import java.nio.file.Path @@ -33,28 +31,20 @@ fun ZipFile.getStream(entryName: String): InputStream { return getInputStream(entry) } -/** - * Common regular assertions for [JarPath]. - */ -fun Assert.isRegular() = all { - transform { it.entries().toList() }.isNotEmpty() - // Close the resource after all assertions are done. - given { it.use(block = {}) } -} - fun Assert.getContent(entryName: String) = transform { it.getContent(entryName) } fun Assert.getMainAttr(name: String) = transform { it.getMainAttr(name) } fun Assert.containsEntries(vararg entries: String) = given { actual -> entries.forEach { entry -> - actual.getEntry(entry) ?: fail("Jar file ${actual.path} does not contain entry $entry") + actual.getEntry(entry) + ?: fail("Jar file ${actual.path} does not contain entry $entry in entries: ${actual.entries().toList()}") } } fun Assert.doesNotContainEntries(vararg entries: String) = given { actual -> entries.forEach { entry -> actual.getEntry(entry) ?: return@forEach - fail("Jar file ${actual.path} contains entry $entry") + fail("Jar file ${actual.path} contains entry $entry in entries: ${actual.entries().toList()}") } } diff --git a/src/functionalTest/resources/test-artifact-1.0-SNAPSHOT.jar b/src/functionalTest/resources/test-artifact-1.0-SNAPSHOT.jar deleted file mode 100644 index 009abad49de5cbf4857e8c429cb1f81c75c2dd56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3115 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 ZiPmb76=Ewli&KF45oX>c2|m`p{{iiGhC2WN From 5ae34fcee94a27cd579f11394548d01765a0130e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Feb 2025 03:43:44 -0500 Subject: [PATCH 228/941] Group ASM and jdependency updates (#1232) --- .github/renovate.json5 | 10 ++++++++++ gradle/libs.versions.toml | 1 + 2 files changed, 11 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 5a9b54152..91920c870 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -6,4 +6,14 @@ "labels": [ "dependencies", ], + "packageRules": [ + { + // https://github.com/tcurdt/jdependency/issues/325 + "groupName": "ASM and jdependency", + "matchPackageNames": [ + "org.vafer:jdependency", + "org.ow2.asm:asm-commons" + ] + } + ] } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 72dcd239e..8676bb457 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,7 @@ apache-commonsIo = "commons-io:commons-io:2.18.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.9" asm = "org.ow2.asm:asm-commons:9.7.1" +# jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.12" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" From f0de4df3455bc161b9851041838bb536dd89d65a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Feb 2025 17:19:44 +0800 Subject: [PATCH 229/941] Remove outdated TODO --- .../github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 9eeb95fc5..2074d7876 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -61,7 +61,6 @@ public abstract class ShadowJar : manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { - // TODO: this has been called but the cache is still working fine, need to investigate why. transformers.get().any { !isCacheableTransform(it::class.java) } || relocators.get().any { !isCacheableRelocator(it::class.java) } } From 8301194bb672493cc3ba28d6c73af353739eb385 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Feb 2025 05:26:13 -0500 Subject: [PATCH 230/941] Write ByteArray directly (#1235) --- .../shadow/transformers/ComponentsXmlResourceTransformer.kt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index c82431639..a44b69ff4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -82,10 +82,7 @@ public open class ComponentsXmlResourceTransformer : Transformer { val entry = ZipEntry(COMPONENTS_XML_PATH) entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) - - transformedResource.inputStream().use { - it.copyTo(os) - } + os.write(transformedResource) components.clear() } From 7a89a9128e73f78aa0e836aaf81bf72e560b3c69 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Feb 2025 23:29:22 -0500 Subject: [PATCH 231/941] Remove KnowsTask as it's useless (#1236) --- api/shadow.api | 11 ------ src/docs/changes/README.md | 1 + .../gradle/plugins/shadow/ShadowBasePlugin.kt | 5 --- .../gradle/plugins/shadow/tasks/KnowsTask.kt | 24 ------------- src/main/resources/shadowBanner.txt | 34 ------------------- 5 files changed, 1 insertion(+), 74 deletions(-) delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt delete mode 100644 src/main/resources/shadowBanner.txt diff --git a/api/shadow.api b/api/shadow.api index 8d93b1465..ce7e384ce 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -224,17 +224,6 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; } -public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask : org/gradle/api/DefaultTask { - public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Companion; - public static final field DESC Ljava/lang/String; - public static final field NAME Ljava/lang/String; - public fun ()V - public final fun knows ()V -} - -public final class com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask$Companion { -} - public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lorg/gradle/api/tasks/util/PatternSet;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ZZ)V diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index ac1d26d49..83fb71b66 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -14,6 +14,7 @@ **Removed** - **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) +- **BREAKING CHANGE:** Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) ## [v9.0.0-beta7] (2025-02-02) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index dd03e4387..38622b7fc 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.tasks.KnowsTask import org.gradle.api.GradleException import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin @@ -17,10 +16,6 @@ public abstract class ShadowBasePlugin : Plugin { } project.extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) project.configurations.create(CONFIGURATION_NAME) - project.tasks.register(KnowsTask.NAME, KnowsTask::class.java) { knows -> - knows.group = GROUP_NAME - knows.description = KnowsTask.DESC - } } public companion object { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt deleted file mode 100644 index 1c5314bba..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/KnowsTask.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText -import org.gradle.api.DefaultTask -import org.gradle.api.tasks.TaskAction - -public abstract class KnowsTask : DefaultTask() { - - @TaskAction - public fun knows() { - logger.info( - """ - No, The Shadow Knows.... - - ${requireResourceAsText("shadowBanner.txt")} - """.trimIndent(), - ) - } - - public companion object { - public const val NAME: String = "knows" - public const val DESC: String = "Do you know who knows?" - } -} diff --git a/src/main/resources/shadowBanner.txt b/src/main/resources/shadowBanner.txt deleted file mode 100644 index 1d231e882..000000000 --- a/src/main/resources/shadowBanner.txt +++ /dev/null @@ -1,34 +0,0 @@ - - . - .MMMMMO .M - .MMMMMMMMM. MMMM. - .MMMMMMMMMMMMMMM. - .MMMMMMMMMMMMM - .MMMMMMMMMMMM - .+MMMMMMMMM,ZMMM. - ...7MM8D8MM.ZMMMMM. - .. MMZ..MZZNMMMMM - .... MMMMMMMZZZ.MMMMMMOOOOOO.. - ... 7MMMMMMMMMZZZMIMMMMOOOOOMMMM.. - .. .~. .MMMMMOMZZZMZMMOOOOOMMMM MM. - .MMMMM ..MMM.7DOMOMOOOOOOOMM MMMMM Z - .. MMMMMMM.. . ...MMMMMMMMMOOOOOOMMMMMMMMMM - . .MMMMMMM. .MMMMM MMMMMOMOMMMMMMMMMMM - MMMMMMMMM .MMM.MMMMMMMMMMMOMMMMMMMMMMM - .MMMMMMMM $MMMM MMMMMMMMMMMMMMMMMM MMM - MMMMMMNMMMMMMMM M.MMMMMM.MMMMMMMM MMMMMM - ..MMMMMMMMMMMMMMMMMMMMMMMMMMMM.MNMMMMMMM . - ...MMMMMMMMMMM MMMMMMMMMMMMM.MMMMMMMM. - MMMMMMMMMM.MMMMMMMMMMMMMDMMMMMMMM. - ..MMMMMMMMMMMMMMMMMMM M,MMMMMMMMMMMMMMMZMMMMM +D , - .:DMM.M. MMMMMMM.MMMMMMMMMMMMMMI:MMMMM :MMO - . MMMMMMMMMMMMMMMMMMMM.MMMMM8 NMMMN - ..MMMMMMMMMMMMMMMMMMMMM MMMMN. - .MMMMMMMMMMMMMMMM. MMM7 , . =. - MMMMMMMMMMMM.$MM M . MM7 - MMMMMMMMM=MI:M8 . MNOM M - MMMMMMMMMM. . - MMMMMM . - +MM - -http://2.bp.blogspot.com/-urTvlwNjLeo/UGg5z9lxw5I/AAAAAAAAHRM/RCbSBi4I60s/s1600/The_Shadow_Knows_by_E_Mann.jpeg \ No newline at end of file From 3a14c1d207f09dc5f152791398f239b885d66c63 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Feb 2025 00:19:30 -0500 Subject: [PATCH 232/941] Refine comments (#1237) --- build.gradle.kts | 4 ++-- gradle.properties | 2 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 2 +- .../gradle/plugins/shadow/ShadowPlugin.kt | 2 +- .../shadow/relocation/SimpleRelocator.kt | 20 +++++++++---------- .../plugins/shadow/tasks/ShadowCopyAction.kt | 9 ++++----- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 6 +++--- .../ApacheNoticeResourceTransformer.kt | 8 ++++---- .../ComponentsXmlResourceTransformer.kt | 8 ++++---- .../Log4j2PluginsCacheFileTransformer.kt | 2 +- .../transformers/PropertiesFileTransformer.kt | 2 +- .../transformers/ServiceFileTransformer.kt | 4 ++-- 12 files changed, 34 insertions(+), 35 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0feaab7ba..ecf92c745 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -90,8 +90,8 @@ testing.suites { // Required to enable `IssueExtension` for all tests. systemProperty("junit.jupiter.extensions.autodetection.enabled", true) - // Required to test configuration cache in tests when using withDebug() - // https://github.com/gradle/gradle/issues/22765#issuecomment-1339427241 + // Required to test configuration cache in tests when using withDebug(). + // See https://github.com/gradle/gradle/issues/22765#issuecomment-1339427241. jvmArgs( "--add-opens", "java.base/java.util=ALL-UNNAMED", diff --git a/gradle.properties b/gradle.properties index 41073c779..702c2ae94 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -# Omit automatic compile dependency on kotlin-stdlib +# Omit automatic compile dependency on kotlin-stdlib. # https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library kotlin.stdlib.default.dependency=false 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 e2e350fc6..6cfd51db1 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 @@ -111,7 +111,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( plugins.withType(JavaGradlePluginPlugin::class.java).configureEach { // Remove the gradleApi so it isn't merged into the jar file. // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. - // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/de + // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161. configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { it.dependencies.remove(dependencies.gradleApi()) } 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 f89b51377..c7c7f908a 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,7 +19,7 @@ public abstract class ShadowPlugin : Plugin { withType(ApplicationPlugin::class.java) { apply(ShadowApplicationPlugin::class.java) } - // Apply the legacy plugin last + // 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. // If the user applies shadow before those plugins. However, this is fine, because this was also diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 9057eedc2..f2e9e2931 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -40,8 +40,8 @@ public open class SimpleRelocator @JvmOverloads constructor( if (rawString) { this.pathPattern = pattern.orEmpty() this.shadedPathPattern = shadedPattern.orEmpty() - this.pattern = "" // not used for raw string relocator - this.shadedPattern = "" // not used for raw string relocator + this.pattern = "" // Not used for raw string relocator. + this.shadedPattern = "" // Not used for raw string relocator. } else { if (pattern == null) { this.pattern = "" @@ -70,15 +70,15 @@ public open class SimpleRelocator @JvmOverloads constructor( } if (!rawString) { - // Create exclude pattern sets for sources + // Create exclude pattern sets for sources. for (exclude in this.excludes.get()) { - // Excludes should be subpackages of the global pattern + // Excludes should be subpackages of the global pattern. if (exclude.startsWith(this.pattern)) { sourcePackageExcludes.add( exclude.substring(this.pattern.length).replaceFirst("[.][*]$".toRegex(), ""), ) } - // Excludes should be subpackages of the global pattern + // Excludes should be subpackages of the global pattern. if (exclude.startsWith(pathPattern)) { sourcePathExcludes.add( exclude.substring(pathPattern.length).replaceFirst("/[*]$".toRegex(), ""), @@ -98,10 +98,10 @@ public open class SimpleRelocator @JvmOverloads constructor( override fun canRelocatePath(path: String): Boolean { if (rawString) return Pattern.compile(pathPattern).matcher(path).find() - // If string is too short - no need to perform expensive string operations + // If string is too short - no need to perform expensive string operations. if (path.length < pathPattern.length) return false var adjustedPath = path.removeSuffix(".class") - // Safeguard against strings containing only ".class" + // Safeguard against strings containing only ".class". if (adjustedPath.isEmpty()) return false // Allow for annoying option of an extra / on the front of a path. See MSHADE-119; // comes from getClass().getResource("/a/b/c.properties"). @@ -172,7 +172,7 @@ public open class SimpleRelocator @JvmOverloads constructor( fun normalizePatterns(patterns: Collection?) = buildSet { patterns ?: return@buildSet for (pattern in patterns) { - // Regex patterns don't need to be normalized and stay as is + // Regex patterns don't need to be normalized and stay as is. if (pattern.startsWith(SelectorUtils.REGEX_HANDLER_PREFIX)) { add(pattern) continue @@ -195,9 +195,9 @@ public open class SimpleRelocator @JvmOverloads constructor( patternTo: String, excludedPatterns: Set, ): String { - // Usually shading makes package names a bit longer, so make buffer 10% bigger than original source + // Usually shading makes package names a bit longer, so make buffer 10% bigger than original source. val shadedSourceContent = StringBuilder(sourceContent.length * 11 / 10) - // Make sure that search pattern starts at word boundary and that we look for literal ".", not regex jokers + // Make sure that search pattern starts at word boundary and that we look for literal ".", not regex jokers. val snippets = sourceContent.split(("\\b" + patternFrom.replace(".", "[.]") + "\\b").toRegex()) .filter(CharSequence::isNotEmpty) snippets.forEachIndexed { i, snippet -> diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 5433e7588..d78bda4b3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -83,8 +83,7 @@ public open class ShadowCopyAction internal constructor( stream.process( object : BaseStreamAction() { override fun visitFile(fileDetails: FileCopyDetails) { - // All project sources are already present, we just need - // to deal with JAR dependencies. + // All project sources are already present, we just need to deal with JAR dependencies. if (isArchive(fileDetails)) { unusedTracker.addDependency(fileDetails.file) } @@ -301,7 +300,7 @@ public open class ShadowCopyAction internal constructor( // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. // Copying the original constant pool should be avoided because it would keep references // to the original class names. This is not a problem at runtime (because these entries in the - // constant pool are never used), but confuses some tools such as Felix' maven-bundle-plugin + // constant pool are never used), but confuses some tools such as Felix's maven-bundle-plugin // that use the constant pool to determine the dependencies of a class. val cw = ClassWriter(0) val cr = ClassReader(classInputStream) @@ -347,7 +346,7 @@ public open class ShadowCopyAction internal constructor( override fun visitDir(dirDetails: FileCopyDetails) { try { - // Trailing slash in name indicates that entry is a directory + // Trailing slash in name indicates that entry is a directory. val path = dirDetails.relativePath.pathString + "/" val archiveEntry = ZipEntry(path) archiveEntry.time = getArchiveTimeFor(dirDetails.lastModified) @@ -402,7 +401,7 @@ public open class ShadowCopyAction internal constructor( return if (segments.size <= 1) { null } else { - // Parent is always a directory so add / to the end of the path + // Parent is always a directory so add / to the end of the path. val parentPath = segments.dropLast(1).joinToString("/") + "/" val entry = setArchiveTimes(ZipEntry(parentPath)) RelativeArchivePath(entry) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 2074d7876..773a84ca6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -56,7 +56,7 @@ public abstract class ShadowJar : private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) init { - // shadow filters out files later. This was the default behavior in Gradle < 6.x + // Shadow filters out files later. This was the default behavior in Gradle < 6.x duplicatesStrategy = DuplicatesStrategy.INCLUDE manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) @@ -155,10 +155,10 @@ public abstract class ShadowJar : @Internal override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest - @Input // TODO: https://github.com/GradleUp/shadow/issues/1202 + @Input // TODO: https://github.com/GradleUp/shadow/issues/1202. override fun getIncludes(): MutableSet = super.getIncludes() - @Input // TODO: https://github.com/GradleUp/shadow/issues/1202 + @Input // TODO: https://github.com/GradleUp/shadow/issues/1202. override fun getExcludes(): MutableSet = super.getExcludes() override fun minimize(): ShadowJar = apply { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 5fa6bf7b9..651456fa3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -94,13 +94,13 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( val year = SimpleDateFormat("yyyy", Locale.US).format(Date()).let { if (inceptionYear != it) "$inceptionYear-$it" else it } - // add headers + // Add headers. if (addHeader) { entries.add("$preamble1$projectName$preamble2") } else { entries.add("") } - // fake second entry, we'll look for a real one later + // Fake second entry, we'll look for a real one later. entries.add("$projectName\nCopyright $year $organizationName\n") entries.add("$preamble3$organizationName ($organizationURL).\n") } @@ -115,7 +115,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( if (!trimmedLine.startsWith("//")) { if (trimmedLine.isNotEmpty()) { if (trimmedLine.startsWith("- ")) { - // resource-bundle 1.3 mode + // resource-bundle 1.3 mode. if (lineCount == 1 && sb.toString().contains("This product includes/uses software(s) developed by")) { currentOrg = organizationEntries.getOrPut(sb.toString().trim()) { TreeSet() } sb.setLength(0) @@ -176,7 +176,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( writer.print('\n') } if (count == 3) { - // do org stuff + // Do org stuff. for ((key, value) in organizationEntries) { writer.print(key) writer.print('\n') diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index a44b69ff4..a2e391b76 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -35,7 +35,7 @@ public open class ComponentsXmlResourceTransformer : Transformer { val bis = object : BufferedInputStream(context.inputStream) { @Throws(IOException::class) override fun close() { - // leave ZIP open + // Leave ZIP open. } } Xpp3DomBuilder.build(XmlStreamReader(bis)) @@ -43,7 +43,7 @@ public open class ComponentsXmlResourceTransformer : Transformer { throw IOException("Error parsing components.xml in ${context.inputStream}", e) } - // Only try to merge in components if there are some elements in the component-set + // Only try to merge in components if there are some elements in the component-set. if (newDom.getChild("components") == null) return val children = newDom.getChild("components").getChildren("component") @@ -59,8 +59,8 @@ public open class ComponentsXmlResourceTransformer : Transformer { setValue(component, "implementation", impl) val key = "$role:$roleHint" - // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing - // configuration carry over + // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing. + // Configuration carry over. components[key]?.getChild("configuration")?.let { component.addChild(it) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index b99559a68..c4784fcc9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -71,7 +71,7 @@ public open class Log4j2PluginsCacheFileTransformer : Transformer { val entry = ZipEntry(PLUGIN_CACHE_FILE) entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) os.putNextEntry(entry) - // prevent the aggregator to close the jar output. + // Prevent the aggregator to close the jar output. aggregator.writeCache(CloseShieldOutputStream.wrap(os)) } finally { deleteTempFiles() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 4ea1b9fed..89cf3eab6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -218,7 +218,7 @@ public open class PropertiesFileTransformer @Inject constructor( } override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - // cannot close the writer as the OutputStream needs to remain open + // Cannot close the writer as the OutputStream needs to remain open. val zipWriter = os.writer(charset) propertiesEntries.forEach { (path, props) -> val entry = ZipEntry(path) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index cf384f2a4..142e64f93 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -82,10 +82,10 @@ public open class ServiceFileTransformer( } } - @Input // TODO: https://github.com/GradleUp/shadow/issues/1202 + @Input // TODO: https://github.com/GradleUp/shadow/issues/1202. override fun getIncludes(): MutableSet = patternSet.includes - @Input // TODO: https://github.com/GradleUp/shadow/issues/1202 + @Input // TODO: https://github.com/GradleUp/shadow/issues/1202. override fun getExcludes(): MutableSet = patternSet.excludes private companion object { From 738a8dbe3c1ad1fac266ff2a01a9afacb1b87449 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Feb 2025 01:34:42 -0500 Subject: [PATCH 233/941] Suppress WRONG_NULLABILITY_FOR_JAVA_OVERRIDE (#1238) --- .../jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index d78bda4b3..1aa537bc4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -397,6 +397,7 @@ public open class ShadowCopyAction internal constructor( ) { public open val isClassFile: Boolean get() = lastName.endsWith(".class") + @Suppress("WRONG_NULLABILITY_FOR_JAVA_OVERRIDE") // It could return null in super.getParent(). override fun getParent(): RelativeArchivePath? { return if (segments.size <= 1) { null From b2b23aee3d929de850fcf416c709632599fd0981 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Feb 2025 03:17:19 -0500 Subject: [PATCH 234/941] Replace Jar attribute key hardcode (#1240) --- .../plugins/shadow/ApplicationPluginTest.kt | 8 +++-- .../gradle/plugins/shadow/JavaPluginTest.kt | 16 ++++++---- .../gradle/plugins/shadow/PublishingTest.kt | 3 +- .../shadow/transformers/TransformersTest.kt | 32 +++++++++++-------- .../plugins/shadow/ShadowApplicationPlugin.kt | 7 ++-- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 5 +-- .../gradle/plugins/shadow/internal/Utils.kt | 11 +++++++ 7 files changed, 52 insertions(+), 30 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 957beda59..e2f79e7f1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -8,6 +8,8 @@ import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.DISTRIBUTION_NAME +import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsEntries @@ -117,7 +119,7 @@ class ApplicationPluginTest : BasePluginTest() { projectBlock = """ shadowJar { manifest { - attributes 'Main-Class': 'shadow.Main2' + attributes '$mainClassAttributeKey': 'shadow.Main2' } } """.trimIndent(), @@ -248,8 +250,8 @@ class ApplicationPluginTest : BasePluginTest() { ) { assertThat(jarPath).useAll { containsEntries(*entriesContained) - getMainAttr("Main-Class").isEqualTo(mainClassAttr) - getMainAttr("Class-Path").isEqualTo(classPathAttr) + getMainAttr(mainClassAttributeKey).isEqualTo(mainClassAttr) + getMainAttr(classPathAttributeKey).isEqualTo(classPathAttr) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 998318b56..f4242c7f8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -10,6 +10,8 @@ import assertk.assertions.isNull import assertk.assertions.isTrue import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest @@ -362,7 +364,7 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) - val value = outputShadowJar.use { it.getMainAttr("Class-Path") } + val value = outputShadowJar.use { it.getMainAttr(classPathAttributeKey) } assertThat(value).isNull() } @@ -378,7 +380,7 @@ class JavaPluginTest : BasePluginTest() { } jar { manifest { - attributes 'Class-Path': '/libs/a.jar' + attributes '$classPathAttributeKey': '/libs/a.jar' } } """.trimIndent(), @@ -386,7 +388,7 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) - val value = outputShadowJar.use { it.getMainAttr("Class-Path") } + val value = outputShadowJar.use { it.getMainAttr(classPathAttributeKey) } assertThat(value).isEqualTo("/libs/a.jar junit-3.8.2.jar") } @@ -405,7 +407,7 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) - val value = outputShadowJar.use { it.getMainAttr("Class-Path") } + val value = outputShadowJar.use { it.getMainAttr(classPathAttributeKey) } assertThat(value).isEqualTo("junit-3.8.2.jar") } @@ -456,7 +458,7 @@ class JavaPluginTest : BasePluginTest() { .single().isEqualTo("my/plugin/MyPlugin.class") transform { it.manifest.mainAttributes }.isNotEmpty() // Doesn't contain Gradle classes. - getMainAttr("Class-Path").isNull() + getMainAttr(classPathAttributeKey).isNull() containsEntries(*entriesInA) doesNotContainEntries(*entriesInB) @@ -482,7 +484,7 @@ class JavaPluginTest : BasePluginTest() { from sourceSets.test.output configurations = [project.configurations.testRuntimeClasspath] manifest { - attributes 'Main-Class': 'shadow.Main' + attributes '$mainClassAttributeKey': 'shadow.Main' } } """.trimIndent(), @@ -496,7 +498,7 @@ class JavaPluginTest : BasePluginTest() { "junit/framework/Test.class", "shadow/Main.class", ) - getMainAttr("Main-Class").isNotNull() + getMainAttr(mainClassAttributeKey).isNotNull() } val pathString = path("build/libs/shadow-1.0-tests.jar").toString() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 0dcaedb5f..0e787c0ce 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -8,6 +8,7 @@ import assertk.assertions.isEmpty import assertk.assertions.isEqualTo import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME +import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata import com.github.jengelman.gradle.plugins.shadow.util.Issue @@ -454,7 +455,7 @@ class PublishingTest : BasePluginTest() { assertThat(jarPath).useAll { containsEntries(*entriesInA) doesNotContainEntries(*entriesInB) - getMainAttr("Class-Path").isEqualTo("b-1.0.jar") + getMainAttr(classPathAttributeKey).isEqualTo("b-1.0.jar") } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 561f8c86d..2ef72bdc7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -6,6 +6,7 @@ import assertk.assertions.isEqualTo import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull import assertk.assertions.isNull +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries @@ -27,8 +28,8 @@ class TransformersTest : BaseTransformerTest() { """ jar { manifest { - attributes 'Main-Class': 'shadow.Main' - attributes 'Test-Entry': 'PASSED' + attributes '$mainClassAttributeKey': 'shadow.Main' + attributes '$TEST_ENTRY_ATTR_KEY': 'PASSED' } } """.trimIndent(), @@ -37,8 +38,8 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) commonAssertions { - assertThat(getValue("Test-Entry")).isEqualTo("PASSED") - assertThat(getValue("Main-Class")).isEqualTo("shadow.Main") + assertThat(getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("PASSED") + assertThat(getValue(mainClassAttributeKey)).isEqualTo("shadow.Main") } } @@ -67,9 +68,9 @@ class TransformersTest : BaseTransformerTest() { val mf = jarPath("build/libs/shadow-1.0.jar").use { it.manifest } assertThat(mf).isNotNull() - assertThat(mf.mainAttributes.getValue("Test-Entry")).isEqualTo("FAILED") - assertThat(mf.mainAttributes.getValue("Main-Class")).isEqualTo("shadow.Main") - assertThat(mf.mainAttributes.getValue("New-Entry")).isNull() + assertThat(mf.mainAttributes.getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("FAILED") + assertThat(mf.mainAttributes.getValue(mainClassAttributeKey)).isEqualTo("shadow.Main") + assertThat(mf.mainAttributes.getValue(NEW_ENTRY_ATTR_KEY)).isNull() } @Issue( @@ -153,9 +154,9 @@ class TransformersTest : BaseTransformerTest() { private fun commonAssertions( mainAttributesBlock: Attributes.() -> Unit = { - assertThat(getValue("Test-Entry")).isEqualTo("PASSED") - assertThat(getValue("Main-Class")).isEqualTo("shadow.Main") - assertThat(getValue("New-Entry")).isEqualTo("NEW") + assertThat(getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("PASSED") + assertThat(getValue(mainClassAttributeKey)).isEqualTo("shadow.Main") + assertThat(getValue(NEW_ENTRY_ATTR_KEY)).isEqualTo("NEW") }, ) { val mf = outputShadowJar.use { it.manifest } @@ -164,17 +165,20 @@ class TransformersTest : BaseTransformerTest() { } private companion object { + const val NEW_ENTRY_ATTR_KEY = "New-Entry" + const val TEST_ENTRY_ATTR_KEY = "Test-Entry" + val MANIFEST_ATTRS = """ jar { manifest { - attributes 'Main-Class': 'shadow.Main' - attributes 'Test-Entry': 'FAILED' + attributes '$mainClassAttributeKey': 'shadow.Main' + attributes '$TEST_ENTRY_ATTR_KEY': 'FAILED' } } $shadowJar { manifest { - attributes 'New-Entry': 'NEW' - attributes 'Test-Entry': 'PASSED' + attributes '$NEW_ENTRY_ATTR_KEY': 'NEW' + attributes '$TEST_ENTRY_ATTR_KEY': 'PASSED' } } """.trimIndent() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 74034b7b4..daf9d3e78 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -6,6 +6,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.applicationExtension import com.github.jengelman.gradle.plugins.shadow.internal.distributions import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension import com.github.jengelman.gradle.plugins.shadow.internal.javaToolchainService +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import org.gradle.api.GradleException import org.gradle.api.Plugin @@ -125,9 +126,9 @@ public abstract class ShadowApplicationPlugin : Plugin { tasks.shadowJar.configure { task -> task.inputs.property("mainClassName", mainClassName) task.doFirst { - // Inject the Main-Class attribute if it is not already present. - if (!task.manifest.attributes.contains("Main-Class")) { - task.manifest.attributes["Main-Class"] = mainClassName.get() + // Inject the attribute if it is not already present. + if (!task.manifest.attributes.contains(mainClassAttributeKey)) { + task.manifest.attributes[mainClassAttributeKey] = mainClassName.get() } } } 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 6cfd51db1..4f147a3ea 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 @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow +import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.jar import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration @@ -43,12 +44,12 @@ public abstract class ShadowJavaPlugin @Inject constructor( task.archiveClassifier.set("all") @Suppress("EagerGradleConfiguration") task.manifest.inheritFrom(jarTask.get().manifest) - val attrProvider = jarTask.map { it.manifest.attributes["Class-Path"]?.toString().orEmpty() } + val attrProvider = jarTask.map { it.manifest.attributes[classPathAttributeKey]?.toString().orEmpty() } val files = files(configurations.shadow) task.doFirst { if (!files.isEmpty) { val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } - task.manifest.attributes["Class-Path"] = attrs.joinToString(" ").trim() + task.manifest.attributes[classPathAttributeKey] = attrs.joinToString(" ").trim() } } task.from(sourceSets.named("main").map { it.output }) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index b98ef3d6a..ee62f471b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -8,6 +8,7 @@ import java.nio.charset.Charset import java.nio.file.NoSuchFileException import java.nio.file.Path import java.util.Properties +import java.util.jar.Attributes.Name as JarAttributeName import kotlin.io.path.inputStream import kotlin.io.path.toPath import org.gradle.api.file.RelativePath @@ -16,6 +17,16 @@ import org.gradle.internal.file.Chmod import org.gradle.internal.file.FileMetadata import org.gradle.internal.file.Stat +/** + * Known as `Main-Class` in the manifest file. + */ +internal val mainClassAttributeKey = JarAttributeName.MAIN_CLASS.toString() + +/** + * Known as `Class-Path` in the manifest file. + */ +internal val classPathAttributeKey = JarAttributeName.CLASS_PATH.toString() + /** * This is used for creating a [DefaultFileTreeElement] with default values. * [file], [chmod], and [stat] should be non-null, so they are set to dummy values here. From 348d340632d48c87d926dd868c7a02b922565d45 Mon Sep 17 00:00:00 2001 From: Andrea Selva Date: Fri, 7 Feb 2025 10:13:55 +0100 Subject: [PATCH 235/941] Fix typo in relocation guide when showing the relocated package name (#1241) --- src/docs/configuration/relocation/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/docs/configuration/relocation/README.md b/src/docs/configuration/relocation/README.md index f94cf07c6..1a0e9dc29 100644 --- a/src/docs/configuration/relocation/README.md +++ b/src/docs/configuration/relocation/README.md @@ -18,9 +18,9 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow ``` The code snippet will rewrite the location for any class in the `junit.framework` to be `shadow.junit`. -For example, the class `junit.textui.TestRunner` becomes `shadow.junit.TestRunner`. -In the resulting JAR, the class file is relocated from `junit/textui/TestRunner.class` to -`shadow/junit/TestRunner.class`. +For example, the class `junit.framework.TestCase` becomes `shadow.junit.TestCase`. +In the resulting JAR, the class file is relocated from `junit/framework/TestCase.class` to +`shadow/junit/TestCase.class`. > Relocation operates at a package level. It is not necessary to specify any patterns for matching, it will operate simply on the prefix From 49fb93f88b9687ab188512f12dd147482855d79f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Feb 2025 04:36:32 -0500 Subject: [PATCH 236/941] Bump min Java requirement to 11 (#1242) --- build.gradle.kts | 6 +++--- src/docs/changes/README.md | 1 + .../gradle/plugins/shadow/transformers/TransformersTest.kt | 1 - .../transformers/Log4j2PluginsCacheFileTransformerTest.kt | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ecf92c745..870336370 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,8 +13,8 @@ plugins { } java { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } kotlin { @@ -22,7 +22,7 @@ kotlin { compilerOptions { // https://docs.gradle.org/current/userguide/compatibility.html#kotlin apiVersion = KotlinVersion.KOTLIN_1_8 - jvmTarget = JvmTarget.JVM_1_8 + jvmTarget = JvmTarget.JVM_11 freeCompilerArgs.addAll( "-Xjvm-default=all", ) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 83fb71b66..a16a0334b 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -10,6 +10,7 @@ **Changed** - **BREAKING CHANGE:** Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) +- Bump min Java requirement to 11. ([#1242](https://github.com/GradleUp/shadow/pull/1242)) **Removed** diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 2ef72bdc7..76bf6d3bf 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -94,7 +94,6 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) val actualFileBytes = outputShadowJar.use { jar -> - @Suppress("Since15") jar.getStream(PLUGIN_CACHE_FILE).use { it.readAllBytes() } } assertThat(actualFileBytes.contentHashCode()).all { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index c37464df7..be119664c 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -80,7 +80,6 @@ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest Date: Fri, 7 Feb 2025 05:14:33 -0500 Subject: [PATCH 237/941] Inject Multi-Release attr if any dependency contains it (#1239) * Add `containsMultiReleaseAttrIfAnyDependencyContains` * Call `findAndAppendMultiReleaseAttr` in `copy` * Update changelog * Fix `failBuildIfProcessingBadJar` * TODO and cleanups --- src/docs/changes/README.md | 1 + .../gradle/plugins/shadow/JavaPluginTest.kt | 25 +++++++++++++++++++ .../gradle/plugins/shadow/internal/Utils.kt | 5 ++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 22 ++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index a16a0334b..db9751af0 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -6,6 +6,7 @@ **Added** - Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) +- Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) **Changed** diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index f4242c7f8..69bef1588 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -12,6 +12,7 @@ import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey +import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest @@ -189,6 +190,30 @@ class JavaPluginTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/449", + ) + @Test + fun containsMultiReleaseAttrIfAnyDependencyContainsIt() { + writeClientAndServerModules() + path("client/build.gradle").appendText( + """ + jar { + manifest { + attributes '$multiReleaseAttributeKey': 'true' + } + } + """.trimIndent() + System.lineSeparator(), + ) + + run(serverShadowJarTask) + + assertThat(outputServerShadowJar).useAll { + transform { it.manifest.mainAttributes }.isNotEmpty() + getMainAttr(multiReleaseAttributeKey).isEqualTo("true") + } + } + @Issue( "https://github.com/GradleUp/shadow/issues/352", "https://github.com/GradleUp/shadow/issues/729", diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index ee62f471b..f8265f3fe 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -27,6 +27,11 @@ internal val mainClassAttributeKey = JarAttributeName.MAIN_CLASS.toString() */ internal val classPathAttributeKey = JarAttributeName.CLASS_PATH.toString() +/** + * Known as `Multi-Release` in the manifest file. + */ +internal val multiReleaseAttributeKey = JarAttributeName.MULTI_RELEASE.toString() + /** * This is used for creating a [DefaultFileTreeElement] with default values. * [file], [chmod], and [stat] should be non-null, so they are set to dummy values here. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 773a84ca6..2beaf3efe 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -9,6 +9,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFil import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.fileCollection +import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import com.github.jengelman.gradle.plugins.shadow.internal.sourceSets @@ -22,6 +23,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransf import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create import java.io.File +import java.io.IOException import java.util.jar.JarFile import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action @@ -263,6 +265,7 @@ public abstract class ShadowJar : @TaskAction override fun copy() { from(includedDependencies) + injectMultiReleaseAttrIfPresent() super.copy() logger.info(stats.toString()) } @@ -324,4 +327,23 @@ public abstract class ShadowJar : } } } + + private fun injectMultiReleaseAttrIfPresent() { + // TODO: https://github.com/GradleUp/shadow/pull/1239#discussion_r1946064032. + val includeMultiReleaseAttr = includedDependencies.files.filter { it.extension == "jar" } + .any { + try { + JarFile(it).use { jarFile -> + // Manifest might be null or the attribute name is invalid, or any other case. + runCatching { jarFile.manifest.mainAttributes.getValue(multiReleaseAttributeKey) }.getOrNull() + } == "true" + } catch (_: IOException) { + // If the jar file is not valid, ignore it. + false + } + } + if (includeMultiReleaseAttr) { + manifest.attributes[multiReleaseAttributeKey] = true + } + } } From 6e26b01598fbbeccce21687ebf3d9dc4cbab4e47 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Feb 2025 06:10:42 -0500 Subject: [PATCH 238/941] Small stuffs for XmlAppendingTransformer and its tests (#1243) * Add XmlAppendingTransformer.java from https://github.com/apache/maven-shade-plugin/blob/49f6e13de5748700ef2185795f89f66be61554ef/src/main/java/org/apache/maven/plugins/shade/resource/XmlAppendingTransformer.java * Convert it to Kotlin * Merge ones * Add a test for issue 168 * Remove `canBundleMetaInfoPluginXml` and cleanups --- .../XmlAppendingTransformerTest.kt | 51 ++++++++++++------- .../transformers/XmlAppendingTransformer.kt | 2 +- 2 files changed, 34 insertions(+), 19 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index 41f215257..b29eec5ab 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import org.junit.jupiter.api.Test @@ -9,7 +10,7 @@ import org.junit.jupiter.api.Test class XmlAppendingTransformerTest : BaseTransformerTest() { @Test fun appendXmlFiles() { - val propertiesXml = "properties.xml" + val xmlEntry = "properties.xml" val xmlContent = """ @@ -17,26 +18,25 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { %s """.trimIndent() - - val xml1 = buildJar("xml1.jar") { - insert(propertiesXml, xmlContent.format("key1", "val1")) + val one = buildJarOne { + insert(xmlEntry, xmlContent.format("key1", "val1")) } - val xml2 = buildJar("xml2.jar") { - insert(propertiesXml, xmlContent.format("key2", "val2")) + val two = buildJarTwo { + insert(xmlEntry, xmlContent.format("key2", "val2")) } projectScriptPath.appendText( transform( - shadowJarBlock = fromJar(xml1, xml2), + shadowJarBlock = fromJar(one, two), transformerBlock = """ - resource = 'properties.xml' + resource = '$xmlEntry' """.trimIndent(), ), ) run(shadowJarTask) - val content = outputShadowJar.use { it.getContent(propertiesXml) }.trimIndent() + val content = outputShadowJar.use { it.getContent(xmlEntry) }.trimIndent() assertThat(content).isEqualTo( """ @@ -49,28 +49,43 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { ) } + @Issue( + "https://github.com/GradleUp/shadow/issues/168", + ) @Test - fun canBundleMetaInfoPluginXml() { - val xmlEntry = "META-INF/plugin.xml" + fun canMergeNestedLevels() { + val xmlEntry = "META-INF/nested.xml" val xmlContent = """ - - my.plugin.id - + %s """.trimIndent() - val pluginJar = buildJar("plugin.jar") { - insert(xmlEntry, xmlContent) + val one = buildJarOne { + insert(xmlEntry, xmlContent.format("")) + } + val two = buildJarTwo { + insert(xmlEntry, xmlContent.format("")) } projectScriptPath.appendText( transform( - shadowJarBlock = fromJar(pluginJar), + shadowJarBlock = fromJar(one, two), + transformerBlock = """ + resource = '$xmlEntry' + """.trimIndent(), ), ) run(shadowJarTask) val content = outputShadowJar.use { it.getContent(xmlEntry) }.trimIndent() - assertThat(content).isEqualTo(xmlContent) + assertThat(content).isEqualTo( + """ + + + + + + """.trimIndent(), + ) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index ed0b8d52d..f60d034de 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -52,7 +52,7 @@ public open class XmlAppendingTransformer @Inject constructor( } }.build(context.inputStream) } catch (e: JDOMException) { - throw RuntimeException("Error processing resource $resource: ${e.message}", e) + throw RuntimeException("Error processing resource ${resource.get()}: ${e.message}", e) } if (doc == null) { From 27dccfdc0137f91672941c42794d7212c32d5108 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Feb 2025 06:37:32 -0500 Subject: [PATCH 239/941] Reuse zipOutputStream in tests (#1244) --- .../Log4j2PluginsCacheFileTransformerTest.kt | 23 ++++++++++--------- .../ServiceFileTransformerTest.kt | 7 +----- .../gradle/plugins/shadow/util/Utils.kt | 6 +++++ 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index be119664c..0a5d3c975 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -9,14 +9,16 @@ import assertk.assertions.isTrue import assertk.assertions.startsWith import assertk.fail import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream -import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.util.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.util.zipOutputStream import java.io.ByteArrayOutputStream -import java.io.File import java.net.URI import java.net.URL import java.util.Collections import java.util.jar.JarInputStream +import kotlin.io.path.createTempFile +import kotlin.io.path.outputStream import org.apache.logging.log4j.core.config.plugins.processor.PluginCache import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE import org.apache.tools.zip.ZipOutputStream @@ -45,15 +47,14 @@ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest - transformer.modifyOutputStream(zipOutputStream, true) + val tempJar = createTempFile("testable-zip-file-", ".jar") + tempJar.outputStream().zipOutputStream().use { zos -> + transformer.modifyOutputStream(zos, true) } // Pull the data back out and make sure it was transformed val cache = PluginCache() - val url = URI("jar:" + testableZipFile.toURI().toURL() + "!/" + PLUGIN_CACHE_FILE).toURL() + val url = URI("jar:" + tempJar.toUri().toURL() + "!/" + PLUGIN_CACHE_FILE).toURL() val resources = Collections.enumeration(listOf(url)) cache.loadCacheFiles(resources) @@ -108,13 +109,13 @@ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest() return TransformerContext(path, input.byteInputStream(), relocators = relocators.toSet(), stats = sharedStats) } - fun OutputStream.zipOutputStream(): ZipOutputStream { - return if (this is ZipOutputStream) this else ZipOutputStream(this) - } - fun ZipFile.getContent(entryName: String): String { return getStream(entryName).bufferedReader().use { it.readText() } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt index 8d8d7c016..ab142567f 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt @@ -1,6 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.util import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import java.io.OutputStream +import org.apache.tools.zip.ZipOutputStream import org.gradle.api.model.ObjectFactory import org.gradle.testfixtures.ProjectBuilder @@ -21,3 +23,7 @@ fun SimpleRelocator( excludes = excludes, rawString = rawString, ) + +fun OutputStream.zipOutputStream(): ZipOutputStream { + return if (this is ZipOutputStream) this else ZipOutputStream(this) +} From 468bc578becfdd4447b0e27c80844148a1a2a84d Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 8 Feb 2025 13:21:11 +0800 Subject: [PATCH 240/941] Prepare version 9.0.0-beta8 --- gradle.properties | 2 +- src/docs/changes/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index 702c2ae94..c2be433d7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta8-SNAPSHOT +VERSION_NAME=9.0.0-beta8 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index db9751af0..0c005dfbb 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased] + +## [v9.0.0-beta8] (2025-02-08) + **Added** - Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) @@ -537,7 +540,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Sat, 8 Feb 2025 13:22:46 +0800 Subject: [PATCH 241/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c2be433d7..d0b5cdb89 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta8 +VERSION_NAME=9.0.0-beta9-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 1ad43607471db2d8269b4514a5c28f8fbacf8c58 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 13 Feb 2025 04:21:10 -0500 Subject: [PATCH 242/941] Improve readability (#1245) * Ignore exceptions * Invert branches in ShadowCopyAction * Rearrange InnerStreamAction * Add isClass and isJar extensions * Rearrange * Remove setArchiveTimes * Replace TransformerContext.getEntryTimestamp with zipEntry * Replace getArchiveTimeFor * Eliminate inner classes * Revert "Eliminate inner classes" This reverts commit 3fa176a096a91f93652bdb1a8a907a6eb0e0844f. * Use extension properties * Remove zipEntry block * Revert "Remove zipEntry block" This reverts commit 7ad026784886ce022e998cd1d6fd51eb90324b5b. * Split RealStreamAction --- api/shadow.api | 12 +- .../shadow/internal/RealStreamAction.kt | 250 ++++++++++++++++ .../gradle/plugins/shadow/internal/Utils.kt | 19 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 277 ++---------------- .../ApacheNoticeResourceTransformer.kt | 6 +- .../transformers/AppendingTransformer.kt | 6 +- .../ComponentsXmlResourceTransformer.kt | 7 +- .../GroovyExtensionModuleTransformer.kt | 7 +- .../IncludeResourceTransformer.kt | 7 +- .../Log4j2PluginsCacheFileTransformer.kt | 7 +- .../ManifestAppenderTransformer.kt | 6 +- .../ManifestResourceTransformer.kt | 7 +- .../transformers/PropertiesFileTransformer.kt | 6 +- .../transformers/ServiceFileTransformer.kt | 6 +- .../shadow/transformers/TransformerContext.kt | 6 - .../transformers/XmlAppendingTransformer.kt | 6 +- .../ApacheNoticeResourceTransformerTest.kt | 2 +- 17 files changed, 314 insertions(+), 323 deletions(-) create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt diff --git a/api/shadow.api b/api/shadow.api index ce7e384ce..9df15508d 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -244,15 +244,15 @@ public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$A public fun getRelativePath ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath; public synthetic fun getRelativePath ()Lorg/gradle/api/file/RelativePath; public fun getSize ()J - public fun isClassFile ()Z + public fun isClass ()Z public fun isDirectory ()Z public fun open ()Ljava/io/InputStream; } public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$BaseStreamAction : org/gradle/api/internal/file/CopyActionProcessingStreamAction { public fun ()V - protected final fun isArchive (Lorg/gradle/api/file/FileCopyDetails;)Z - protected final fun isClass (Lorg/gradle/api/file/FileCopyDetails;)Z + protected fun isClass (Lorg/gradle/api/file/FileCopyDetails;)Z + protected fun isJar (Lorg/gradle/api/file/FileCopyDetails;)Z public fun processFile (Lorg/gradle/api/internal/file/copy/FileCopyDetailsInternal;)V protected fun visitDir (Lorg/gradle/api/file/FileCopyDetails;)V protected abstract fun visitFile (Lorg/gradle/api/file/FileCopyDetails;)V @@ -263,14 +263,14 @@ public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAc } public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath : org/gradle/api/file/RelativePath { - public fun (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction;Lorg/apache/tools/zip/ZipEntry;)V + public fun (Lorg/apache/tools/zip/ZipEntry;Z)V public final fun charAt (I)C public fun get (I)C public fun getEntry ()Lorg/apache/tools/zip/ZipEntry; public fun getLength ()I public fun getParent ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath; public synthetic fun getParent ()Lorg/gradle/api/file/RelativePath; - public fun isClassFile ()Z + public fun isClass ()Z public final fun length ()I } @@ -583,7 +583,6 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans public final fun copy (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; public fun equals (Ljava/lang/Object;)Z - public static final fun getEntryTimestamp (ZJ)J public final fun getInputStream ()Ljava/io/InputStream; public final fun getPath ()Ljava/lang/String; public final fun getRelocators ()Ljava/util/Set; @@ -603,7 +602,6 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion { public final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; - public final fun getEntryTimestamp (ZJ)J } public class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt new file mode 100644 index 000000000..fe095fcf0 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt @@ -0,0 +1,250 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.ArchiveFileTreeElement +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.BaseStreamAction +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.RelativeArchivePath +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext +import java.io.File +import java.io.InputStream +import java.util.zip.ZipException +import org.apache.tools.zip.UnixStat +import org.apache.tools.zip.ZipFile +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.GradleException +import org.gradle.api.file.FileCopyDetails +import org.gradle.api.file.FileTreeElement +import org.gradle.api.file.RelativePath +import org.gradle.api.tasks.util.PatternSet +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.commons.ClassRemapper +import org.slf4j.Logger + +internal class RealStreamAction( + private val zipOutStr: ZipOutputStream, + encoding: String?, + private val transformers: Set, + private val relocators: Set, + private val patternSet: PatternSet, + private val unused: Set, + private val stats: ShadowStats, + private val zipFile: File, + private val preserveFileTimestamps: Boolean, + private val logger: Logger, +) : BaseStreamAction() { + private val remapper = RelocatorRemapper(relocators, stats) + private val visitedFiles = mutableSetOf() + + init { + if (encoding != null) { + this.zipOutStr.setEncoding(encoding) + } + } + + override fun visitFile(fileDetails: FileCopyDetails) { + if (fileDetails.isJar) { + processArchive(fileDetails) + } else { + try { + val isClass = fileDetails.isClass + if (!remapper.hasRelocators() || !isClass) { + if (isTransformable(fileDetails)) { + transform(fileDetails) + } else { + val mappedPath = remapper.map(fileDetails.relativePath.pathString) + val entry = zipEntry(mappedPath, preserveFileTimestamps, fileDetails.lastModified) { + unixMode = UnixStat.FILE_FLAG or fileDetails.permissions.toUnixNumeric() + } + zipOutStr.putNextEntry(entry) + fileDetails.copyTo(zipOutStr) + zipOutStr.closeEntry() + } + } else if (isClass && !isUnused(fileDetails.path)) { + remapClass(fileDetails) + } + recordVisit(fileDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $fileDetails to ZIP '$zipFile'.", e) + } + } + } + + override fun visitDir(dirDetails: FileCopyDetails) { + try { + // Trailing slash in name indicates that entry is a directory. + val path = dirDetails.relativePath.pathString + "/" + val entry = zipEntry(path, preserveFileTimestamps, dirDetails.lastModified) { + unixMode = UnixStat.DIR_FLAG or dirDetails.permissions.toUnixNumeric() + } + zipOutStr.putNextEntry(entry) + zipOutStr.closeEntry() + recordVisit(dirDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $dirDetails to ZIP '$zipFile'.", e) + } + } + + private fun recordVisit(path: RelativePath): Boolean { + return visitedFiles.add(path.pathString) + } + + private fun processArchive(fileDetails: FileCopyDetails) { + stats.startJar() + ZipFile(fileDetails.file).use { archive -> + archive.entries.asSequence() + .map { + ArchiveFileTreeElement(RelativeArchivePath(it, preserveFileTimestamps)) + } + .filter { + patternSet.asSpec.isSatisfiedBy(it.asFileTreeElement()) + }.forEach { archiveElement -> + if (archiveElement.relativePath.isFile) { + visitArchiveFile(archiveElement, archive) + } + } + } + stats.finishJar() + } + + private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { + if (recordVisit(archiveDir)) { + zipOutStr.putNextEntry(archiveDir.entry) + zipOutStr.closeEntry() + } + } + + private fun visitArchiveFile(archiveFile: ArchiveFileTreeElement, archive: ZipFile) { + val archiveFilePath = archiveFile.relativePath + if (archiveFile.isClass || !isTransformable(archiveFile)) { + if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { + if (!remapper.hasRelocators() || !archiveFile.isClass) { + copyArchiveEntry(archiveFilePath, archive) + } else { + remapClass(archiveFilePath, archive) + } + } + } else { + transform(archiveFile, archive) + } + } + + private fun addParentDirectories(file: RelativeArchivePath?) { + file?.let { + addParentDirectories(it.parent) + if (!it.isFile) { + visitArchiveDirectory(it) + } + } + } + + private fun isUnused(classPath: String): Boolean { + val classPathWithoutExtension = classPath.substringBeforeLast(".") + val className = classPathWithoutExtension.replace('/', '.') + val result = unused.contains(className) + if (result) { + logger.debug("Dropping unused class: $className") + } + return result + } + + private fun remapClass(file: RelativeArchivePath, archive: ZipFile) { + if (file.isClass) { + val entry = zipEntry(remapper.mapPath(file) + CLASS_SUFFIX, preserveFileTimestamps) + addParentDirectories(RelativeArchivePath(entry, preserveFileTimestamps)) + remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) + } + } + + private fun remapClass(fileCopyDetails: FileCopyDetails) { + if (fileCopyDetails.isClass) { + fileCopyDetails.file.inputStream().use { + remapClass(it, fileCopyDetails.path, fileCopyDetails.lastModified) + } + } + } + + /** + * Applies remapping to the given class with the specified relocation path. The remapped class is then written + * to the zip file. [classInputStream] is closed automatically to prevent future file leaks. + * See #364 and #408. + */ + private fun remapClass(classInputStream: InputStream, path: String, lastModified: Long) { + // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. + // Copying the original constant pool should be avoided because it would keep references + // to the original class names. This is not a problem at runtime (because these entries in the + // constant pool are never used), but confuses some tools such as Felix's maven-bundle-plugin + // that use the constant pool to determine the dependencies of a class. + val cw = ClassWriter(0) + val cr = ClassReader(classInputStream) + val cv = ClassRemapper(cw, remapper) + + try { + cr.accept(cv, ClassReader.EXPAND_FRAMES) + } catch (t: Throwable) { + throw GradleException("Error in ASM processing class $path", t) + } + + val renamedClass = cw.toByteArray() + // Temporarily remove the multi-release prefix. + val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() + val newPath = path.replace(multiReleasePrefix, "") + val mappedName = multiReleasePrefix + remapper.mapPath(newPath) + try { + // Now we put it back on so the class file is written out with the right extension. + zipOutStr.putNextEntry(zipEntry("$mappedName.class", preserveFileTimestamps, lastModified)) + renamedClass.inputStream().use { + it.copyTo(zipOutStr) + } + zipOutStr.closeEntry() + } catch (_: ZipException) { + logger.warn("We have a duplicate $mappedName in source project") + } + } + + private fun copyArchiveEntry(archiveFile: RelativeArchivePath, archive: ZipFile) { + val mappedPath = remapper.map(archiveFile.entry.name) + val entry = zipEntry(mappedPath, preserveFileTimestamps, archiveFile.entry.time) + val mappedFile = RelativeArchivePath(entry, preserveFileTimestamps) + addParentDirectories(mappedFile) + zipOutStr.putNextEntry(mappedFile.entry) + archive.getInputStream(archiveFile.entry).use { + it.copyTo(zipOutStr) + } + zipOutStr.closeEntry() + } + + private fun transform(element: ArchiveFileTreeElement, archive: ZipFile) { + transformAndClose(element, archive.getInputStream(element.relativePath.entry)) + } + + private fun transform(details: FileCopyDetails) { + transformAndClose(details, details.file.inputStream()) + } + + private fun transformAndClose(element: FileTreeElement, inputStream: InputStream) { + inputStream.use { steam -> + val mappedPath = remapper.map(element.relativePath.pathString) + transformers.find { + it.canTransformResource(element) + }?.transform( + TransformerContext( + path = mappedPath, + inputStream = steam, + relocators = relocators, + stats = stats, + ), + ) + } + } + + private fun isTransformable(element: FileTreeElement): Boolean { + return transformers.any { it.canTransformResource(element) } + } + + companion object { + const val CLASS_SUFFIX = ".class" + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index f8265f3fe..8c90053ad 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.io.File @@ -9,8 +10,8 @@ import java.nio.file.NoSuchFileException import java.nio.file.Path import java.util.Properties import java.util.jar.Attributes.Name as JarAttributeName -import kotlin.io.path.inputStream import kotlin.io.path.toPath +import org.apache.tools.zip.ZipEntry import org.gradle.api.file.RelativePath import org.gradle.api.internal.file.DefaultFileTreeElement import org.gradle.internal.file.Chmod @@ -32,6 +33,22 @@ internal val classPathAttributeKey = JarAttributeName.CLASS_PATH.toString() */ internal val multiReleaseAttributeKey = JarAttributeName.MULTI_RELEASE.toString() +internal inline fun zipEntry( + name: String, + preserveLastModified: Boolean = true, + lastModified: Long = -1, + block: ZipEntry.() -> Unit = {}, +): ZipEntry = ZipEntry(name).apply { + if (preserveLastModified) { + if (lastModified >= 0) { + time = lastModified + } + } else { + time = ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES + } + block() +} + /** * This is used for creating a [DefaultFileTreeElement] with default values. * [file], [chmod], and [stat] should be non-null, so they are set to dummy values here. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 1aa537bc4..404f57100 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -1,23 +1,21 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowStats -import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper +import com.github.jengelman.gradle.plugins.shadow.internal.RealStreamAction +import com.github.jengelman.gradle.plugins.shadow.internal.RealStreamAction.Companion.CLASS_SUFFIX import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import java.io.File import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.util.GregorianCalendar -import java.util.zip.ZipException -import org.apache.tools.zip.UnixStat import org.apache.tools.zip.Zip64RequiredException import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipFile import org.apache.tools.zip.ZipOutputStream import org.gradle.api.GradleException import org.gradle.api.file.FileCopyDetails @@ -34,9 +32,6 @@ import org.gradle.api.tasks.WorkResult import org.gradle.api.tasks.WorkResults import org.gradle.api.tasks.bundling.Zip import org.gradle.api.tasks.util.PatternSet -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.commons.ClassRemapper import org.slf4j.LoggerFactory public open class ShadowCopyAction internal constructor( @@ -84,7 +79,7 @@ public open class ShadowCopyAction internal constructor( object : BaseStreamAction() { override fun visitFile(fileDetails: FileCopyDetails) { // All project sources are already present, we just need to deal with JAR dependencies. - if (isArchive(fileDetails)) { + if (fileDetails.isJar) { unusedTracker.addDependency(fileDetails.file) } } @@ -104,7 +99,7 @@ public open class ShadowCopyAction internal constructor( try { zipOutStream.use { outputStream -> stream.process( - StreamAction( + RealStreamAction( outputStream, encoding, transformers, @@ -112,6 +107,9 @@ public open class ShadowCopyAction internal constructor( patternSet, unusedClasses, stats, + zipFile, + preserveFileTimestamps, + logger, ), ) processTransformers(outputStream) @@ -137,265 +135,27 @@ public open class ShadowCopyAction internal constructor( } } - private fun getArchiveTimeFor(timestamp: Long): Long { - return if (preserveFileTimestamps) timestamp else CONSTANT_TIME_FOR_ZIP_ENTRIES - } - - private fun setArchiveTimes(zipEntry: ZipEntry): ZipEntry { - if (!preserveFileTimestamps) { - zipEntry.time = CONSTANT_TIME_FOR_ZIP_ENTRIES - } - return zipEntry - } - public abstract class BaseStreamAction : CopyActionProcessingStreamAction { - protected fun isArchive(fileDetails: FileCopyDetails): Boolean { - return fileDetails.relativePath.pathString.endsWith(".jar") - } - - protected fun isClass(fileDetails: FileCopyDetails): Boolean { - return fileDetails.path.endsWith(".class") - } - override fun processFile(details: FileCopyDetailsInternal) { if (details.isDirectory) visitDir(details) else visitFile(details) } protected open fun visitDir(dirDetails: FileCopyDetails) {} - protected abstract fun visitFile(fileDetails: FileCopyDetails) - } - - private inner class StreamAction( - private val zipOutStr: ZipOutputStream, - encoding: String?, - private val transformers: Set, - private val relocators: Set, - private val patternSet: PatternSet, - private val unused: Set, - private val stats: ShadowStats, - ) : BaseStreamAction() { - private val remapper = RelocatorRemapper(relocators, stats) - private val visitedFiles = mutableSetOf() - - init { - if (encoding != null) { - this.zipOutStr.setEncoding(encoding) - } - } - - override fun visitFile(fileDetails: FileCopyDetails) { - if (!isArchive(fileDetails)) { - try { - val isClass = isClass(fileDetails) - if (!remapper.hasRelocators() || !isClass) { - if (!isTransformable(fileDetails)) { - val mappedPath = remapper.map(fileDetails.relativePath.pathString) - val archiveEntry = ZipEntry(mappedPath) - archiveEntry.time = getArchiveTimeFor(fileDetails.lastModified) - archiveEntry.unixMode = UnixStat.FILE_FLAG or fileDetails.permissions.toUnixNumeric() - zipOutStr.putNextEntry(archiveEntry) - fileDetails.copyTo(zipOutStr) - zipOutStr.closeEntry() - } else { - transform(fileDetails) - } - } else if (isClass && !isUnused(fileDetails.path)) { - remapClass(fileDetails) - } - recordVisit(fileDetails.relativePath) - } catch (e: Exception) { - throw GradleException("Could not add $fileDetails to ZIP '$zipFile'.", e) - } - } else { - processArchive(fileDetails) - } - } - - private fun recordVisit(path: RelativePath): Boolean { - return visitedFiles.add(path.pathString) - } - - private fun processArchive(fileDetails: FileCopyDetails) { - stats.startJar() - ZipFile(fileDetails.file).use { archive -> - archive.entries.asSequence() - .map { - ArchiveFileTreeElement(RelativeArchivePath(it)) - } - .filter { - patternSet.asSpec.isSatisfiedBy(it.asFileTreeElement()) - }.forEach { archiveElement -> - if (archiveElement.relativePath.isFile) { - visitArchiveFile(archiveElement, archive) - } - } - } - stats.finishJar() - } - - private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { - if (recordVisit(archiveDir)) { - zipOutStr.putNextEntry(archiveDir.entry) - zipOutStr.closeEntry() - } - } - - private fun visitArchiveFile(archiveFile: ArchiveFileTreeElement, archive: ZipFile) { - val archiveFilePath = archiveFile.relativePath - if (archiveFile.isClassFile || !isTransformable(archiveFile)) { - if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { - if (!remapper.hasRelocators() || !archiveFile.isClassFile) { - copyArchiveEntry(archiveFilePath, archive) - } else { - remapClass(archiveFilePath, archive) - } - } - } else { - transform(archiveFile, archive) - } - } - private fun addParentDirectories(file: RelativeArchivePath?) { - file?.let { - addParentDirectories(it.parent) - if (!it.isFile) { - visitArchiveDirectory(it) - } - } - } - - private fun isUnused(classPath: String): Boolean { - val classPathWithoutExtension = classPath.substringBeforeLast(".") - val className = classPathWithoutExtension.replace('/', '.') - val result = unused.contains(className) - if (result) { - logger.debug("Dropping unused class: $className") - } - return result - } - - private fun remapClass(file: RelativeArchivePath, archive: ZipFile) { - if (file.isClassFile) { - val zipEntry = setArchiveTimes(ZipEntry(remapper.mapPath(file) + ".class")) - addParentDirectories(RelativeArchivePath(zipEntry)) - remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) - } - } - - private fun remapClass(fileCopyDetails: FileCopyDetails) { - if (fileCopyDetails.name.endsWith(".class")) { - fileCopyDetails.file.inputStream().use { - remapClass(it, fileCopyDetails.path, fileCopyDetails.lastModified) - } - } - } - - /** - * Applies remapping to the given class with the specified relocation path. The remapped class is then written - * to the zip file. [classInputStream] is closed automatically to prevent future file leaks. - * See #364 and #408. - */ - private fun remapClass(classInputStream: InputStream, path: String, lastModified: Long) { - // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. - // Copying the original constant pool should be avoided because it would keep references - // to the original class names. This is not a problem at runtime (because these entries in the - // constant pool are never used), but confuses some tools such as Felix's maven-bundle-plugin - // that use the constant pool to determine the dependencies of a class. - val cw = ClassWriter(0) - val cr = ClassReader(classInputStream) - val cv = ClassRemapper(cw, remapper) - - try { - cr.accept(cv, ClassReader.EXPAND_FRAMES) - } catch (t: Throwable) { - throw GradleException("Error in ASM processing class $path", t) - } - - val renamedClass = cw.toByteArray() - // Temporarily remove the multi-release prefix. - val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() - val newPath = path.replace(multiReleasePrefix, "") - val mappedName = multiReleasePrefix + remapper.mapPath(newPath) - try { - // Now we put it back on so the class file is written out with the right extension. - val archiveEntry = ZipEntry("$mappedName.class") - archiveEntry.time = getArchiveTimeFor(lastModified) - zipOutStr.putNextEntry(archiveEntry) - renamedClass.inputStream().use { - it.copyTo(zipOutStr) - } - zipOutStr.closeEntry() - } catch (ignored: ZipException) { - logger.warn("We have a duplicate $mappedName in source project") - } - } - - private fun copyArchiveEntry(archiveFile: RelativeArchivePath, archive: ZipFile) { - val mappedPath = remapper.map(archiveFile.entry.name) - val entry = ZipEntry(mappedPath) - entry.time = getArchiveTimeFor(archiveFile.entry.time) - val mappedFile = RelativeArchivePath(entry) - addParentDirectories(mappedFile) - zipOutStr.putNextEntry(mappedFile.entry) - archive.getInputStream(archiveFile.entry).use { - it.copyTo(zipOutStr) - } - zipOutStr.closeEntry() - } - - override fun visitDir(dirDetails: FileCopyDetails) { - try { - // Trailing slash in name indicates that entry is a directory. - val path = dirDetails.relativePath.pathString + "/" - val archiveEntry = ZipEntry(path) - archiveEntry.time = getArchiveTimeFor(dirDetails.lastModified) - archiveEntry.unixMode = UnixStat.DIR_FLAG or dirDetails.permissions.toUnixNumeric() - zipOutStr.putNextEntry(archiveEntry) - zipOutStr.closeEntry() - recordVisit(dirDetails.relativePath) - } catch (e: Exception) { - throw GradleException("Could not add $dirDetails to ZIP '$zipFile'.", e) - } - } - - private fun transform(element: ArchiveFileTreeElement, archive: ZipFile) { - transformAndClose(element, archive.getInputStream(element.relativePath.entry)) - } - - private fun transform(details: FileCopyDetails) { - transformAndClose(details, details.file.inputStream()) - } - - private fun transformAndClose(element: FileTreeElement, inputStream: InputStream) { - inputStream.use { steam -> - val mappedPath = remapper.map(element.relativePath.pathString) - transformers.find { - it.canTransformResource(element) - }?.transform( - TransformerContext( - path = mappedPath, - inputStream = steam, - relocators = relocators, - stats = stats, - ), - ) - } - } - - private fun isTransformable(element: FileTreeElement): Boolean { - return transformers.any { it.canTransformResource(element) } - } + protected open val FileCopyDetails.isClass: Boolean get() = relativePath.pathString.endsWith(CLASS_SUFFIX) + protected open val FileCopyDetails.isJar: Boolean get() = relativePath.pathString.endsWith(".jar") } - public open inner class RelativeArchivePath( + public open class RelativeArchivePath( public open val entry: ZipEntry, + private val preserveFileTimestamps: Boolean, ) : RelativePath( !entry.isDirectory, // `dir/` will be split into ["dir", ""], we have to trim empty segments here. *entry.name.split('/').filter(CharSequence::isNotEmpty).toTypedArray(), ) { - public open val isClassFile: Boolean get() = lastName.endsWith(".class") + public open val isClass: Boolean get() = lastName.endsWith(CLASS_SUFFIX) @Suppress("WRONG_NULLABILITY_FOR_JAVA_OVERRIDE") // It could return null in super.getParent(). override fun getParent(): RelativeArchivePath? { @@ -404,8 +164,10 @@ public open class ShadowCopyAction internal constructor( } else { // Parent is always a directory so add / to the end of the path. val parentPath = segments.dropLast(1).joinToString("/") + "/" - val entry = setArchiveTimes(ZipEntry(parentPath)) - RelativeArchivePath(entry) + RelativeArchivePath( + zipEntry(parentPath, preserveFileTimestamps), + preserveFileTimestamps, + ) } } } @@ -413,7 +175,7 @@ public open class ShadowCopyAction internal constructor( public open class ArchiveFileTreeElement( private val archivePath: RelativeArchivePath, ) : FileTreeElement { - public open val isClassFile: Boolean get() = archivePath.isClassFile + public open val isClass: Boolean get() = archivePath.isClass override fun isDirectory(): Boolean = archivePath.entry.isDirectory @@ -437,11 +199,8 @@ public open class ShadowCopyAction internal constructor( } override fun getFile(): File = throw UnsupportedOperationException() - override fun open(): InputStream = throw UnsupportedOperationException() - override fun copyTo(outputStream: OutputStream): Unit = throw UnsupportedOperationException() - override fun copyTo(file: File): Boolean = throw UnsupportedOperationException() } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 651456fa3..e9fafdc53 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.property +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.io.PrintWriter import java.nio.charset.Charset import java.text.SimpleDateFormat @@ -8,7 +9,6 @@ import java.util.Date import java.util.Locale import java.util.TreeSet import javax.inject.Inject -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory @@ -158,9 +158,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { val copyright = copyright.orNull - val zipEntry = ZipEntry(NOTICE_PATH) - zipEntry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, zipEntry.time) - os.putNextEntry(zipEntry) + os.putNextEntry(zipEntry(NOTICE_PATH, preserveFileTimestamps)) val writer = PrintWriter(os.writer(charset)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index 7b3dd768b..c0fdcfa8a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -1,9 +1,9 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.property +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.io.ByteArrayOutputStream import javax.inject.Inject -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory @@ -55,9 +55,7 @@ public open class AppendingTransformer @Inject constructor( } override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(resource.get()) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(resource.get(), preserveFileTimestamps)) // Closing a ByteArrayOutputStream has no effect, so we don't use a use block here. data.toByteArray().inputStream().copyTo(os) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index a2e391b76..d21bc44e4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -1,11 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp import java.io.BufferedInputStream import java.io.ByteArrayOutputStream import java.io.IOException -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.codehaus.plexus.util.xml.XmlStreamReader import org.codehaus.plexus.util.xml.XmlStreamWriter @@ -79,9 +78,7 @@ public open class ComponentsXmlResourceTransformer : Transformer { } override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(COMPONENTS_XML_PATH) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(COMPONENTS_XML_PATH, preserveFileTimestamps)) os.write(transformedResource) components.clear() } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index e001dc5b6..2a1dec80e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -1,9 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.inputStream -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.util.Properties -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement @@ -77,9 +76,7 @@ public open class GroovyExtensionModuleTransformer : Transformer { override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { val name = if (legacy) PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR else PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR - val entry = ZipEntry(name) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(name, preserveFileTimestamps)) module.inputStream().use { it.copyTo(os) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index bd0646161..c3f903b10 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -1,9 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.property -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import javax.inject.Inject -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.RegularFileProperty import org.gradle.api.model.ObjectFactory @@ -34,9 +33,7 @@ public open class IncludeResourceTransformer @Inject constructor( override fun hasTransformedResource(): Boolean = file.get().asFile.exists() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(resource.get()) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(resource.get(), preserveFileTimestamps)) file.get().asFile.inputStream().use { inputStream -> inputStream.copyTo(os) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index c4784fcc9..280b7a344 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -1,9 +1,9 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.ShadowStats +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp import java.net.URL import java.nio.file.Path import java.util.Collections @@ -14,7 +14,6 @@ import kotlin.io.path.outputStream import org.apache.commons.io.output.CloseShieldOutputStream import org.apache.logging.log4j.core.config.plugins.processor.PluginCache import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement @@ -68,9 +67,7 @@ public open class Log4j2PluginsCacheFileTransformer : Transformer { val aggregator = PluginCache() aggregator.loadCacheFiles(urlEnumeration) relocatePlugins(aggregator) - val entry = ZipEntry(PLUGIN_CACHE_FILE) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(PLUGIN_CACHE_FILE, preserveFileTimestamps)) // Prevent the aggregator to close the jar output. aggregator.writeCache(CloseShieldOutputStream.wrap(os)) } finally { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 8617b6847..ee7a7e605 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -1,11 +1,11 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.setProperty +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.io.ByteArrayOutputStream import java.io.IOException import java.util.jar.JarFile.MANIFEST_NAME import javax.inject.Inject -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory @@ -51,9 +51,7 @@ public open class ManifestAppenderTransformer @Inject constructor( override fun hasTransformedResource(): Boolean = attributes.get().isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(MANIFEST_NAME) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(MANIFEST_NAME, preserveFileTimestamps)) os.write(manifestContents) if (attributes.get().isNotEmpty()) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 5c9c8dd75..2ca483e27 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -2,13 +2,12 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext.Companion.getEntryTimestamp +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.io.IOException import java.util.jar.Attributes import java.util.jar.JarFile import java.util.jar.Manifest import javax.inject.Inject -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory @@ -78,9 +77,7 @@ public open class ManifestResourceTransformer @Inject constructor( attributes[Attributes.Name(key)] = value } - val entry = ZipEntry(JarFile.MANIFEST_NAME) - entry.time = getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(JarFile.MANIFEST_NAME, preserveFileTimestamps)) manifest!!.write(os) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 89cf3eab6..1d5a076b1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -5,12 +5,12 @@ import com.github.jengelman.gradle.plugins.shadow.internal.inputStream import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.setProperty +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import java.io.InputStream import java.nio.charset.Charset import java.util.Properties import javax.inject.Inject -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory @@ -221,9 +221,7 @@ public open class PropertiesFileTransformer @Inject constructor( // Cannot close the writer as the OutputStream needs to remain open. val zipWriter = os.writer(charset) propertiesEntries.forEach { (path, props) -> - val entry = ZipEntry(path) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(path, preserveFileTimestamps)) props.inputStream(charset).bufferedReader(charset).use { it.copyTo(zipWriter) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 142e64f93..d633d9334 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -1,9 +1,9 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.tasks.Input @@ -74,9 +74,7 @@ public open class ServiceFileTransformer( override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { serviceEntries.forEach { (path, data) -> - val entry = ZipEntry(path) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(path, preserveFileTimestamps)) os.write(data.joinToString("\n").toByteArray()) os.closeEntry() } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt index cefd226a0..1ee33cd0c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import java.io.InputStream public data class TransformerContext @JvmOverloads constructor( @@ -32,10 +31,5 @@ public data class TransformerContext @JvmOverloads constructor( public companion object { @JvmStatic public fun builder(): Builder = Builder() - - @JvmStatic - public fun getEntryTimestamp(preserveFileTimestamps: Boolean, entryTime: Long): Long { - return if (preserveFileTimestamps) entryTime else ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES - } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index f60d034de..f201acbde 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -1,9 +1,9 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.property +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.io.StringReader import javax.inject.Inject -import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory @@ -75,9 +75,7 @@ public open class XmlAppendingTransformer @Inject constructor( override fun hasTransformedResource(): Boolean = doc != null override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val entry = ZipEntry(resource.get()) - entry.time = TransformerContext.getEntryTimestamp(preserveFileTimestamps, entry.time) - os.putNextEntry(entry) + os.putNextEntry(zipEntry(resource.get(), preserveFileTimestamps)) XMLOutputter(Format.getPrettyFormat()).output(doc, os) doc = null } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt index 40ea4493d..566dc5c78 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -68,7 +68,7 @@ class ApacheNoticeResourceTransformerTest : BaseTransformerTest Date: Thu, 13 Feb 2025 04:39:07 -0500 Subject: [PATCH 243/941] Replace loggers (#1246) --- .../gradle/plugins/shadow/internal/RealStreamAction.kt | 2 +- .../jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 4 ++-- .../shadow/transformers/ManifestAppenderTransformer.kt | 4 ++-- .../shadow/transformers/ManifestResourceTransformer.kt | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt index fe095fcf0..44548364a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt @@ -17,11 +17,11 @@ import org.gradle.api.GradleException import org.gradle.api.file.FileCopyDetails import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath +import org.gradle.api.logging.Logger import org.gradle.api.tasks.util.PatternSet import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.commons.ClassRemapper -import org.slf4j.Logger internal class RealStreamAction( private val zipOutStr: ZipOutputStream, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 404f57100..bc2b08d1d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -28,11 +28,11 @@ import org.gradle.api.internal.file.DefaultFilePermissions import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.CopyActionProcessingStream import org.gradle.api.internal.file.copy.FileCopyDetailsInternal +import org.gradle.api.logging.Logging import org.gradle.api.tasks.WorkResult import org.gradle.api.tasks.WorkResults import org.gradle.api.tasks.bundling.Zip import org.gradle.api.tasks.util.PatternSet -import org.slf4j.LoggerFactory public open class ShadowCopyAction internal constructor( private val zipFile: File, @@ -205,7 +205,7 @@ public open class ShadowCopyAction internal constructor( } public companion object { - private val logger = LoggerFactory.getLogger(ShadowCopyAction::class.java) + private val logger = Logging.getLogger(ShadowCopyAction::class.java) public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index ee7a7e605..64e287a0c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -8,10 +8,10 @@ import java.util.jar.JarFile.MANIFEST_NAME import javax.inject.Inject import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.gradle.api.logging.Logging import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input -import org.slf4j.LoggerFactory /** * A resource processor that can append arbitrary attributes to the first MANIFEST.MF @@ -71,7 +71,7 @@ public open class ManifestAppenderTransformer @Inject constructor( } private companion object { - private val logger = LoggerFactory.getLogger(ManifestAppenderTransformer::class.java) + private val logger = Logging.getLogger(ManifestAppenderTransformer::class.java) private val EOL = "\r\n".toByteArray() private val SEPARATOR = ": ".toByteArray() } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 2ca483e27..9ebbe3ea8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -10,12 +10,12 @@ import java.util.jar.Manifest import javax.inject.Inject import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement +import org.gradle.api.logging.Logging import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input import org.gradle.api.tasks.Optional -import org.slf4j.LoggerFactory /** * A resource processor that allows the arbitrary addition of attributes to @@ -86,6 +86,6 @@ public open class ManifestResourceTransformer @Inject constructor( } private companion object { - private val logger = LoggerFactory.getLogger(ManifestResourceTransformer::class.java) + private val logger = Logging.getLogger(ManifestResourceTransformer::class.java) } } From eb53e3fe8e38df6c18e487f288fa593b08833729 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 14 Feb 2025 09:14:05 +0800 Subject: [PATCH 244/941] Update plugin android-lint to v8.8.1 (#1247) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8676bb457..6fcc3ada7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,6 +32,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = "org.jetbrains.kotlin.jvm:2.1.10" -android-lint = "com.android.lint:8.8.0" +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 e9c416bc52497dc87da365808386623ee93090cf Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 13 Feb 2025 23:02:55 -0500 Subject: [PATCH 245/941] Mark Transformer as throwing IOException (#1248) --- src/docs/changes/README.md | 1 + .../gradle/plugins/shadow/transformers/Transformer.kt | 3 +++ .../plugins/shadow/transformers/XmlAppendingTransformer.kt | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 0c005dfbb..57e04fe38 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -10,6 +10,7 @@ - Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) - Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) +- Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) **Changed** diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index 93fcd0e21..935472ff2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import java.io.IOException import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Named import org.gradle.api.file.FileTreeElement @@ -17,10 +18,12 @@ import org.gradle.api.tasks.Internal public interface Transformer : Named { public fun canTransformResource(element: FileTreeElement): Boolean + @Throws(IOException::class) public fun transform(context: TransformerContext) public fun hasTransformedResource(): Boolean + @Throws(IOException::class) public fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) @Internal diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index f201acbde..a01790a39 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry +import java.io.IOException import java.io.StringReader import javax.inject.Inject import org.apache.tools.zip.ZipOutputStream @@ -52,7 +53,7 @@ public open class XmlAppendingTransformer @Inject constructor( } }.build(context.inputStream) } catch (e: JDOMException) { - throw RuntimeException("Error processing resource ${resource.get()}: ${e.message}", e) + throw IOException("Error processing resource ${resource.get()}: ${e.message}", e) } if (doc == null) { From dfda481c98cf22ec5c329aa1d6ac4d9b9375a5bf Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 13 Feb 2025 23:20:24 -0500 Subject: [PATCH 246/941] Update ShadowCopyAction related logic and kdoc from Gradle (#1249) * Update ShadowCopyAction refs https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java * Update DefaultZipCompressor from https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DefaultZipCompressor.java * Update ZipCompressor from https://github.com/gradle/gradle/blob/73091267320cd330bcb3457903436579bac354ce/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/ZipCompressor.java * Update RealStreamAction from https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java#L89 * Update line baseline --- api/shadow.api | 3 +- lint-baseline.xml | 41 +++++++------------ .../shadow/internal/DefaultZipCompressor.kt | 7 +++- .../shadow/internal/RealStreamAction.kt | 3 ++ .../plugins/shadow/internal/ZipCompressor.kt | 19 ++++++++- .../plugins/shadow/tasks/ShadowCopyAction.kt | 21 ++++++---- 6 files changed, 54 insertions(+), 40 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 9df15508d..c85a64485 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -101,7 +101,8 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/inter public abstract fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; } -public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor : org/gradle/api/internal/file/archive/compression/ArchiveOutputStreamFactory { +public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor { + public abstract fun createArchiveOutputStream (Ljava/io/File;)Lorg/apache/tools/zip/ZipOutputStream; } public abstract class com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin : org/gradle/api/Plugin { diff --git a/lint-baseline.xml b/lint-baseline.xml index 494ab0b7c..18c75952d 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -1,5 +1,5 @@ - + @@ -52,7 +52,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -63,7 +63,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -74,7 +74,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -85,7 +85,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -96,7 +96,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -107,7 +107,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +118,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,7 +129,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +140,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -151,7 +151,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -162,7 +162,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -173,7 +173,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,18 +184,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - - - - diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt index 926df8351..8414e617c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt @@ -5,6 +5,9 @@ import org.apache.tools.zip.Zip64Mode import org.apache.tools.zip.ZipOutputStream import org.gradle.api.UncheckedIOException +/** + * Modified from [org.gradle.api.internal.file.copy.DefaultZipCompressor.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DefaultZipCompressor.java). + */ internal class DefaultZipCompressor( allowZip64Mode: Boolean, private val entryCompressionMethod: Int, @@ -12,8 +15,8 @@ internal class DefaultZipCompressor( private val zip64Mode = if (allowZip64Mode) Zip64Mode.AsNeeded else Zip64Mode.Never override fun createArchiveOutputStream(destination: File): ZipOutputStream { - try { - return ZipOutputStream(destination).apply { + return try { + ZipOutputStream(destination).apply { setUseZip64(zip64Mode) setMethod(entryCompressionMethod) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt index 44548364a..835d54698 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt @@ -23,6 +23,9 @@ import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.commons.ClassRemapper +/** + * Modified from [org.gradle.api.internal.file.archive.ZipCopyAction.StreamAction](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java#L89). + */ internal class RealStreamAction( private val zipOutStr: ZipOutputStream, encoding: String?, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt index a5037af28..14a56204b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt @@ -1,5 +1,20 @@ package com.github.jengelman.gradle.plugins.shadow.internal -import org.gradle.api.internal.file.archive.compression.ArchiveOutputStreamFactory +import java.io.File +import java.io.IOException +import org.apache.tools.zip.ZipOutputStream -public interface ZipCompressor : ArchiveOutputStreamFactory +/** + * Compresses the input. + * + * Modified from [org.gradle.api.internal.file.copy.ZipCompressor.java](https://github.com/gradle/gradle/blob/73091267320cd330bcb3457903436579bac354ce/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/ZipCompressor.java). + */ +public fun interface ZipCompressor { + /** + * Returns the output stream that is able to compress into the destination file + * + * @param destination the destination of the archive output stream + */ + @Throws(IOException::class) + public fun createArchiveOutputStream(destination: File): ZipOutputStream +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index bc2b08d1d..9dad0f602 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -10,7 +10,6 @@ import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import java.io.File -import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.util.GregorianCalendar @@ -34,6 +33,9 @@ import org.gradle.api.tasks.WorkResults import org.gradle.api.tasks.bundling.Zip import org.gradle.api.tasks.util.PatternSet +/** + * Modified from [org.gradle.api.internal.file.archive.ZipCopyAction.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java). + */ public open class ShadowCopyAction internal constructor( private val zipFile: File, private val compressor: ZipCompressor, @@ -91,16 +93,16 @@ public open class ShadowCopyAction internal constructor( } val zipOutStream = try { - compressor.createArchiveOutputStream(zipFile) as ZipOutputStream + compressor.createArchiveOutputStream(zipFile) } catch (e: Exception) { - throw GradleException("Could not create ZIP '$zipFile'", e) + throw GradleException("Could not create ZIP '$zipFile'.", e) } try { - zipOutStream.use { outputStream -> + zipOutStream.use { zos -> stream.process( RealStreamAction( - outputStream, + zos, encoding, transformers, relocators, @@ -112,25 +114,26 @@ public open class ShadowCopyAction internal constructor( logger, ), ) - processTransformers(outputStream) + processTransformers(zos) } - } catch (e: IOException) { + } catch (e: Exception) { if (e.cause is Zip64RequiredException) { throw Zip64RequiredException( "${e.cause?.message}\n\nTo build this archive, please enable the zip64 extension.\n" + "See: ${documentationRegistry.getDslRefForProperty(Zip::class.java, "zip64")}", ) } + zipFile.delete() // Rethrow the exception like `java.util.zip.ZipException: archive is not a ZIP archive`. throw e } return WorkResults.didWork(true) } - private fun processTransformers(stream: ZipOutputStream) { + private fun processTransformers(zos: ZipOutputStream) { transformers.forEach { transformer -> if (transformer.hasTransformedResource()) { - transformer.modifyOutputStream(stream, preserveFileTimestamps) + transformer.modifyOutputStream(zos, preserveFileTimestamps) } } } From 3283cd2cbeb4e270221c3c8b4e9a3f3c382981f8 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 14 Feb 2025 12:21:09 +0800 Subject: [PATCH 247/941] Rearrange changelog item --- src/docs/changes/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 57e04fe38..b35a2bc22 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Added** + +- Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) + ## [v9.0.0-beta8] (2025-02-08) @@ -10,7 +14,6 @@ - Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) - Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) -- Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) **Changed** From 0d9b9d83a4301b1b83081fa66b0e3b6119a1848c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 14 Feb 2025 05:20:19 -0500 Subject: [PATCH 248/941] Correct AbstractCopyTask.from usages in functional tests (#1250) Local dependency jars should be included by `implementation` instead of `from`, `from` should copy the input files into the output shadowed jar without any unzipping. --- .../gradle/plugins/shadow/BasePluginTest.kt | 18 ++++++++++-------- .../shadow/caching/ShadowJarCachingTest.kt | 10 +++++----- .../transformers/AppendingTransformerTest.kt | 14 +++++++++----- .../GroovyExtensionModuleTransformerTest.kt | 8 +++++--- .../transformers/ServiceFileTransformerTest.kt | 16 +++++++++++----- .../shadow/transformers/TransformersTest.kt | 2 +- .../XmlAppendingTransformerTest.kt | 4 ++-- 7 files changed, 43 insertions(+), 29 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 a00f7f7f5..cfa754c47 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 @@ -362,21 +362,23 @@ abstract class BasePluginTest { fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } - fun fromJar(vararg paths: Path): String { - return paths.joinToString(System.lineSeparator()) { "from('${it.toUri().toURL().path}')" } + fun implementationFiles(vararg paths: Path): String { + return paths.joinToString(System.lineSeparator()) { "implementation files('${it.toUri().toURL().path}')" } } inline fun transform( - shadowJarBlock: String = "", + dependenciesBlock: String = "", transformerBlock: String = "", ): String { return """ - $shadowJar { - $shadowJarBlock - transform(${T::class.java.name}) { - $transformerBlock + dependencies { + $dependenciesBlock + } + $shadowJar { + transform(${T::class.java.name}) { + $transformerBlock + } } - } """.trimIndent() } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 4e982969f..fa2172025 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -18,8 +18,8 @@ class ShadowJarCachingTest : BaseCachingTest() { fun shadowJarIsCachedCorrectlyWhenCopying() { projectScriptPath.appendText( """ - $shadowJar { - ${fromJar(artifactAJar, artifactBJar)} + dependencies { + ${implementationFiles(artifactAJar, artifactBJar)} } """.trimIndent(), ) @@ -29,7 +29,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } val replaced = projectScriptPath.readText().lines() - .filterNot { it == fromJar(artifactBJar) } + .filterNot { it == implementationFiles(artifactBJar) } .joinToString(System.lineSeparator()) projectScriptPath.writeText(replaced) @@ -43,8 +43,8 @@ class ShadowJarCachingTest : BaseCachingTest() { fun shadowJarIsCachedCorrectlyWhenOutputFileIsChanged() { projectScriptPath.appendText( """ - $shadowJar { - ${fromJar(artifactAJar, artifactBJar)} + dependencies { + ${implementationFiles(artifactAJar, artifactBJar)} } """.trimIndent() + System.lineSeparator(), ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 496f99a2e..499389a03 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -17,14 +17,16 @@ class AppendingTransformerTest : BaseTransformerTest() { } val config = if (shortSyntax) { """ + dependencies { + ${implementationFiles(one, two)} + } $shadowJar { - ${fromJar(one, two)} append('$ENTRY_TEST_PROPERTIES') } """.trimIndent() } else { transform( - shadowJarBlock = fromJar(one, two), + dependenciesBlock = implementationFiles(one, two), transformerBlock = """ resource = '$ENTRY_TEST_PROPERTIES' """.trimIndent(), @@ -50,22 +52,24 @@ class AppendingTransformerTest : BaseTransformerTest() { } val config = if (shortSyntax) { """ + dependencies { + ${implementationFiles(one, two)} + } $shadowJar { - ${fromJar(one, two)} append('resources/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') append('resources/config/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') } """.trimIndent() } else { val block1 = transform( - shadowJarBlock = fromJar(one, two), + dependenciesBlock = implementationFiles(one, two), transformerBlock = """ resource = 'resources/$APPLICATION_YML_FILE' separator = '$APPLICATION_YML_SEPARATOR' """.trimIndent(), ) val block2 = transform( - shadowJarBlock = fromJar(one, two), + dependenciesBlock = implementationFiles(one, two), transformerBlock = """ resource = 'resources/config/$APPLICATION_YML_FILE' separator = '$APPLICATION_YML_SEPARATOR' diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index 8a45b1cea..a61c0c541 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -21,14 +21,16 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { fun groovyExtensionModuleTransformer(shortSyntax: Boolean) { val config = if (shortSyntax) { """ + dependencies { + ${implementationFiles(buildJarFoo(), buildJarBar())} + } $shadowJar { - ${fromJar(buildJarFoo(), buildJarBar())} mergeGroovyExtensionModules() } """.trimIndent() } else { transform( - shadowJarBlock = fromJar(buildJarFoo(), buildJarBar()), + dependenciesBlock = implementationFiles(buildJarFoo(), buildJarBar()), ) } projectScriptPath.appendText(config) @@ -42,7 +44,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { fun groovyExtensionModuleTransformerWorksForLegacyGroovy() { projectScriptPath.appendText( transform( - shadowJarBlock = fromJar( + dependenciesBlock = implementationFiles( buildJarFoo(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), buildJarBar(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), ), diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 85ff7e13d..b204e1cb6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -14,8 +14,10 @@ class ServiceFileTransformerTest : BaseTransformerTest() { fun serviceResourceTransformer(shortSyntax: Boolean) { val config = if (shortSyntax) { """ + dependencies { + ${implementationFiles(buildJarOne(), buildJarTwo())} + } $shadowJar { - ${fromJar(buildJarOne(), buildJarTwo())} mergeServiceFiles { exclude 'META-INF/services/com.acme.*' } @@ -23,7 +25,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """.trimIndent() } else { transform( - shadowJarBlock = fromJar(buildJarOne(), buildJarTwo()), + dependenciesBlock = implementationFiles(buildJarOne(), buildJarTwo()), transformerBlock = """ exclude 'META-INF/services/com.acme.*' """.trimIndent(), @@ -49,14 +51,16 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } val config = if (shortSyntax) { """ + dependencies { + ${implementationFiles(one, two)} + } $shadowJar { - ${fromJar(one, two)} mergeServiceFiles("META-INF/foo") } """.trimIndent() } else { transform( - shadowJarBlock = fromJar(one, two), + dependenciesBlock = implementationFiles(one, two), transformerBlock = """ path = 'META-INF/foo' """.trimIndent(), @@ -109,8 +113,10 @@ class ServiceFileTransformerTest : BaseTransformerTest() { projectScriptPath.appendText( """ + dependencies { + ${implementationFiles(one, two)} + } $shadowJar { - ${fromJar(one, two)} mergeServiceFiles() relocate("org.apache", "myapache") { exclude 'org.apache.axis.components.compiler.Jikes' diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 76bf6d3bf..6019be919 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -87,7 +87,7 @@ class TransformersTest : BaseTransformerTest() { } projectScriptPath.appendText( transform( - shadowJarBlock = fromJar(one, two), + dependenciesBlock = implementationFiles(one, two), ), ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index b29eec5ab..d3397a491 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -27,7 +27,7 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { projectScriptPath.appendText( transform( - shadowJarBlock = fromJar(one, two), + dependenciesBlock = implementationFiles(one, two), transformerBlock = """ resource = '$xmlEntry' """.trimIndent(), @@ -68,7 +68,7 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { projectScriptPath.appendText( transform( - shadowJarBlock = fromJar(one, two), + dependenciesBlock = implementationFiles(one, two), transformerBlock = """ resource = '$xmlEntry' """.trimIndent(), From d4a42882749945f4ecfd7337bb17e96342ccfb5d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 14 Feb 2025 05:34:36 -0500 Subject: [PATCH 249/941] Defer testPluginClasspath (#1251) --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 870336370..5196939d0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,7 +46,7 @@ spotless { } } -val testPluginClasspath: Configuration by configurations.creating { +val testPluginClasspath by configurations.registering { isCanBeResolved = true } From 363e912243e6e1108c809e9ab64a0bbd839b5943 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Feb 2025 12:33:54 +0000 Subject: [PATCH 250/941] Bump elliptic from 6.5.4 to 6.6.1 (#1253) Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.4 to 6.6.1. - [Commits](https://github.com/indutny/elliptic/compare/v6.5.4...v6.6.1) --- updated-dependencies: - dependency-name: elliptic dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 597cac2ef..129ed3aa3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3500,9 +3500,9 @@ electron-to-chromium@^1.4.477: integrity sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A== elliptic@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== + version "6.6.1" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" + integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== dependencies: bn.js "^4.11.9" brorand "^1.1.0" From f902cd15e374d58097edb430f96424646e1ebe71 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Feb 2025 09:23:34 +0800 Subject: [PATCH 251/941] Bump cross-spawn from 6.0.5 to 6.0.6 (#1254) Bumps [cross-spawn](https://github.com/moxystudio/node-cross-spawn) from 6.0.5 to 6.0.6. - [Changelog](https://github.com/moxystudio/node-cross-spawn/blob/v6.0.6/CHANGELOG.md) - [Commits](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.5...v6.0.6) --- updated-dependencies: - dependency-name: cross-spawn dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 129ed3aa3..a9c15b28f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2946,9 +2946,9 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: sha.js "^2.4.8" cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== + version "6.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57" + integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw== dependencies: nice-try "^1.0.4" path-key "^2.0.1" From d35f47e1e126051b274ba8eda657c6d4889c0491 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 16 Feb 2025 22:10:40 -0500 Subject: [PATCH 252/941] Add a test for non-cacheable transformer (#1255) * Inline isCacheableTransform and isCacheableRelocator * Add shadowJarCacheDisabledIfAnyTransformerIsNotCacheable * Reuse cleanOutputs * Add one more trans into the test case --- .../plugins/shadow/caching/BaseCachingTest.kt | 21 +++++----- .../shadow/caching/TransformerCachingTest.kt | 38 +++++++++++++++++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 13 ++----- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt index c6bce26f1..5a527a21f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt @@ -21,15 +21,7 @@ import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE abstract class BaseCachingTest : BasePluginTest() { open val taskPath: String = shadowJarTask - fun assertExecutionSuccess(vararg outputs: String) { - // task was executed and not pulled from cache - assertRunWithResult(SUCCESS, outputs = outputs) - } - - /** - * This should be called after [assertExecutionSuccess] to ensure that the [taskPath] is cached. - */ - fun assertExecutionsFromCacheAndUpToDate(vararg outputs: String) { + fun cleanOutputs() { run("clean") // Make sure the output shadow jar has been deleted. assertFailure { outputShadowJar.close() }.isInstanceOf(NoSuchFileException::class) @@ -37,7 +29,18 @@ abstract class BaseCachingTest : BasePluginTest() { val buildDirs = projectRoot.walk().filter { it.isDirectory() && it.name == "build" } // Make sure build folders are deleted by clean task. assertThat(buildDirs).isEmpty() + } + fun assertExecutionSuccess(vararg outputs: String) { + // The task was executed and not pulled from cache. + assertRunWithResult(SUCCESS, outputs = outputs) + } + + /** + * This should be called after [assertExecutionSuccess] to ensure that the [taskPath] is cached. + */ + fun assertExecutionsFromCacheAndUpToDate(vararg outputs: String) { + cleanOutputs() // Run the task again to ensure it is pulled from cache. assertRunWithResult(FROM_CACHE, outputs = outputs) // Run the task again to ensure it is up-to-date. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 2d11b0d53..245bb1d04 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -12,6 +12,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.IncludeResourceTr import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestAppenderTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.NoOpTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer @@ -120,6 +121,43 @@ class TransformerCachingTest : BaseCachingTest() { assertions("baz") } + @Test + fun shadowJarCacheDisabledIfAnyTransformerIsNotCacheable() { + projectScriptPath.appendText( + """ + $shadowJar { + mergeServiceFiles() + } + """.trimIndent() + System.lineSeparator(), + ) + + assertCompositeExecutions() + + projectScriptPath.appendText( + """ + $shadowJar { + mergeGroovyExtensionModules() + } + """.trimIndent() + System.lineSeparator(), + ) + + assertCompositeExecutions() + + projectScriptPath.appendText( + """ + $shadowJar { + // Use NoOpTransformer to mock a custom transformer here, it's not cacheable. + transform(${NoOpTransformer::class.java.name}.INSTANCE) + } + """.trimIndent(), + ) + + assertExecutionSuccess() + cleanOutputs() + // The shadowJar task should be executed again as the cache is disabled. + assertExecutionSuccess() + } + @ParameterizedTest @MethodSource("transformerConfigProvider") fun shadowJarIsCachedCorrectlyWhenUsingOtherTransformers(pair: Pair>) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 2beaf3efe..2160d3b48 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -25,6 +25,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Compa import java.io.File import java.io.IOException import java.util.jar.JarFile +import kotlin.reflect.full.hasAnnotation import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action import org.gradle.api.artifacts.Configuration @@ -63,8 +64,8 @@ public abstract class ShadowJar : manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { - transformers.get().any { !isCacheableTransform(it::class.java) } || - relocators.get().any { !isCacheableRelocator(it::class.java) } + transformers.get().any { !it::class.hasAnnotation() } || + relocators.get().any { !it::class.hasAnnotation() } } } @@ -302,14 +303,6 @@ public abstract class ShadowJar : transformers.add(transformer) } - private fun isCacheableRelocator(clazz: Class): Boolean { - return clazz.isAnnotationPresent(CacheableRelocator::class.java) - } - - private fun isCacheableTransform(clazz: Class): Boolean { - return clazz.isAnnotationPresent(CacheableTransformer::class.java) - } - private val packageRelocators: List get() { if (!enableRelocation.get()) return emptyList() From 5509b552a3e6f5cd9048faad9f97505c8ecfc95e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 16 Feb 2025 22:25:15 -0500 Subject: [PATCH 253/941] Merge kt files (#1256) --- .../plugins/shadow/relocation/CacheableRelocator.kt | 10 ---------- .../gradle/plugins/shadow/relocation/Relocator.kt | 13 +++++++++++++ .../shadow/transformers/CacheableTransformer.kt | 10 ---------- .../plugins/shadow/transformers/Transformer.kt | 12 ++++++++++++ 4 files changed, 25 insertions(+), 20 deletions(-) delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt deleted file mode 100644 index e78d7a777..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/CacheableRelocator.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -/** - * Marks that a given instance of [Relocator] is compatible with the Gradle build cache. - * In other words, it has its appropriate inputs annotated so that Gradle can consider them when - * determining the cache key. - */ -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.CLASS) -public annotation class CacheableRelocator diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt index 11cda6395..d6b48d295 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -1,5 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation +import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer + /** * Modified from [org.apache.maven.plugins.shade.relocation.Relocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java). * @@ -21,3 +23,14 @@ public interface Relocator { public val ROLE: String = Relocator::class.java.name } } + +/** + * Marks that a given instance of [Relocator] is compatible with the Gradle build cache. + * In other words, it has its appropriate inputs annotated so that Gradle can consider them when + * determining the cache key. + * + * @see CacheableTransformer + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +public annotation class CacheableRelocator diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt deleted file mode 100644 index a240b27e9..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.transformers - -/** - * Marks that a given instance of [Transformer] is compatible with the Gradle build cache. - * In other words, it has its appropriate inputs annotated so that Gradle can consider them when - * determining the cache key. - */ -@Retention(AnnotationRetention.RUNTIME) -@Target(AnnotationTarget.CLASS) -public annotation class CacheableTransformer diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index 935472ff2..e7f459e77 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import java.io.IOException import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Named @@ -54,6 +55,17 @@ public interface Transformer : Named { } } +/** + * Marks that a given instance of [Transformer] is compatible with the Gradle build cache. + * In other words, it has its appropriate inputs annotated so that Gradle can consider them when + * determining the cache key. + * + * @see CacheableRelocator + */ +@Retention(AnnotationRetention.RUNTIME) +@Target(AnnotationTarget.CLASS) +public annotation class CacheableTransformer + public object NoOpTransformer : Transformer { public override fun canTransformResource(element: FileTreeElement): Boolean = false public override fun transform(context: TransformerContext): Unit = Unit From c40f6e9b9034793c93d4e779612dda1869aa1bea Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Feb 2025 02:39:50 -0500 Subject: [PATCH 254/941] Move tracking unused classes logic out of ShadowCopyAction (#1257) --- api/shadow.api | 2 +- src/docs/changes/README.md | 4 ++ .../shadow/internal/RealStreamAction.kt | 4 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 47 +------------------ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 13 +++-- 5 files changed, 17 insertions(+), 53 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index c85a64485..e0747646d 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -227,7 +227,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; - public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lorg/gradle/api/tasks/util/PatternSet;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ZZ)V + public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lorg/gradle/api/tasks/util/PatternSet;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ZLjava/util/Set;)V public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; } diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index b35a2bc22..3aa239e25 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -7,6 +7,10 @@ - Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) +**Changed** + +- **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) + ## [v9.0.0-beta8] (2025-02-08) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt index 835d54698..ae3c04f17 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt @@ -32,7 +32,7 @@ internal class RealStreamAction( private val transformers: Set, private val relocators: Set, private val patternSet: PatternSet, - private val unused: Set, + private val unusedClasses: Set, private val stats: ShadowStats, private val zipFile: File, private val preserveFileTimestamps: Boolean, @@ -146,7 +146,7 @@ internal class RealStreamAction( private fun isUnused(classPath: String): Boolean { val classPathWithoutExtension = classPath.substringBeforeLast(".") val className = classPathWithoutExtension.replace('/', '.') - val result = unused.contains(className) + val result = unusedClasses.contains(className) if (result) { logger.debug("Dropping unused class: $className") } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 9dad0f602..d8b3e0e35 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.RealStreamAction import com.github.jengelman.gradle.plugins.shadow.internal.RealStreamAction.Companion.CLASS_SUFFIX -import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry @@ -36,7 +35,7 @@ import org.gradle.api.tasks.util.PatternSet /** * Modified from [org.gradle.api.internal.file.archive.ZipCopyAction.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java). */ -public open class ShadowCopyAction internal constructor( +public open class ShadowCopyAction( private val zipFile: File, private val compressor: ZipCompressor, private val documentationRegistry: DocumentationRegistry, @@ -46,52 +45,10 @@ public open class ShadowCopyAction internal constructor( private val patternSet: PatternSet, private val stats: ShadowStats, private val preserveFileTimestamps: Boolean, - private val minimizeJar: Boolean, - private val unusedTracker: UnusedTracker?, + private val unusedClasses: Set, ) : CopyAction { - public constructor( - zipFile: File, - compressor: ZipCompressor, - documentationRegistry: DocumentationRegistry, - encoding: String?, - transformers: Set, - relocators: Set, - patternSet: PatternSet, - stats: ShadowStats, - preserveFileTimestamps: Boolean, - minimizeJar: Boolean, - ) : this( - zipFile, - compressor, - documentationRegistry, - encoding, - transformers, - relocators, - patternSet, - stats, - preserveFileTimestamps, - minimizeJar, - null, - ) - override fun execute(stream: CopyActionProcessingStream): WorkResult { - val unusedClasses = if (minimizeJar && unusedTracker != null) { - stream.process( - object : BaseStreamAction() { - override fun visitFile(fileDetails: FileCopyDetails) { - // All project sources are already present, we just need to deal with JAR dependencies. - if (fileDetails.isJar) { - unusedTracker.addDependency(fileDetails.file) - } - } - }, - ) - unusedTracker.findUnused() - } else { - emptySet() - } - val zipOutStream = try { compressor.createArchiveOutputStream(zipFile) } catch (e: Exception) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 2160d3b48..27934da76 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -273,10 +273,14 @@ public abstract class ShadowJar : override fun createCopyAction(): CopyAction { val documentationRegistry = services.get(DocumentationRegistry::class.java) - val unusedTracker = if (minimizeJar.get()) { - UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) + val unusedClasses = if (minimizeJar.get()) { + val unusedTracker = UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) + includedDependencies.files.forEach { + unusedTracker.addDependency(it) + } + unusedTracker.findUnused() } else { - null + emptySet() } return ShadowCopyAction( archiveFile.get().asFile, @@ -288,8 +292,7 @@ public abstract class ShadowJar : rootPatternSet, stats, isPreserveFileTimestamps, - minimizeJar.get(), - unusedTracker, + unusedClasses, ) } From 433dae0b884f947c30fcaf7542476b4616904abe Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Feb 2025 03:21:51 -0500 Subject: [PATCH 255/941] Remove BaseStreamAction (#1258) --- api/shadow.api | 9 ---- lint-baseline.xml | 48 +++++++++---------- src/docs/changes/README.md | 4 ++ .../shadow/internal/RealStreamAction.kt | 16 +++++-- .../plugins/shadow/tasks/ShadowCopyAction.kt | 15 ------ 5 files changed, 40 insertions(+), 52 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index e0747646d..bb7bc55f2 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -250,15 +250,6 @@ public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$A public fun open ()Ljava/io/InputStream; } -public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$BaseStreamAction : org/gradle/api/internal/file/CopyActionProcessingStreamAction { - public fun ()V - protected fun isClass (Lorg/gradle/api/file/FileCopyDetails;)Z - protected fun isJar (Lorg/gradle/api/file/FileCopyDetails;)Z - public fun processFile (Lorg/gradle/api/internal/file/copy/FileCopyDetailsInternal;)V - protected fun visitDir (Lorg/gradle/api/file/FileCopyDetails;)V - protected abstract fun visitFile (Lorg/gradle/api/file/FileCopyDetails;)V -} - public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion { public final fun getCONSTANT_TIME_FOR_ZIP_ENTRIES ()J } diff --git a/lint-baseline.xml b/lint-baseline.xml index 18c75952d..d8e6256d5 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -37,66 +37,66 @@ + errorLine1="import org.gradle.api.internal.file.CopyActionProcessingStreamAction" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.file.copy.FileCopyDetailsInternal" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.DocumentationRegistry" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.file.DefaultFilePermissions" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.file.copy.CopyAction" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.file.copy.CopyActionProcessingStream" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -107,7 +107,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +118,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,7 +129,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +140,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 3aa239e25..2c713a3be 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -11,6 +11,10 @@ - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) +**Removed** + +- **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) + ## [v9.0.0-beta8] (2025-02-08) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt index ae3c04f17..8fb034bb1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.internal import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.ArchiveFileTreeElement -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.BaseStreamAction import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.RelativeArchivePath import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext @@ -17,6 +16,8 @@ import org.gradle.api.GradleException import org.gradle.api.file.FileCopyDetails import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath +import org.gradle.api.internal.file.CopyActionProcessingStreamAction +import org.gradle.api.internal.file.copy.FileCopyDetailsInternal import org.gradle.api.logging.Logger import org.gradle.api.tasks.util.PatternSet import org.objectweb.asm.ClassReader @@ -37,7 +38,7 @@ internal class RealStreamAction( private val zipFile: File, private val preserveFileTimestamps: Boolean, private val logger: Logger, -) : BaseStreamAction() { +) : CopyActionProcessingStreamAction { private val remapper = RelocatorRemapper(relocators, stats) private val visitedFiles = mutableSetOf() @@ -47,7 +48,11 @@ internal class RealStreamAction( } } - override fun visitFile(fileDetails: FileCopyDetails) { + override fun processFile(details: FileCopyDetailsInternal) { + if (details.isDirectory) visitDir(details) else visitFile(details) + } + + private fun visitFile(fileDetails: FileCopyDetails) { if (fileDetails.isJar) { processArchive(fileDetails) } else { @@ -75,7 +80,7 @@ internal class RealStreamAction( } } - override fun visitDir(dirDetails: FileCopyDetails) { + private fun visitDir(dirDetails: FileCopyDetails) { try { // Trailing slash in name indicates that entry is a directory. val path = dirDetails.relativePath.pathString + "/" @@ -249,5 +254,8 @@ internal class RealStreamAction( companion object { const val CLASS_SUFFIX = ".class" + + private val FileCopyDetails.isClass: Boolean get() = relativePath.pathString.endsWith(CLASS_SUFFIX) + private val FileCopyDetails.isJar: Boolean get() = relativePath.pathString.endsWith(".jar") } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index d8b3e0e35..a0247a6ed 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -16,16 +16,13 @@ import org.apache.tools.zip.Zip64RequiredException import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.GradleException -import org.gradle.api.file.FileCopyDetails import org.gradle.api.file.FilePermissions import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath import org.gradle.api.internal.DocumentationRegistry -import org.gradle.api.internal.file.CopyActionProcessingStreamAction import org.gradle.api.internal.file.DefaultFilePermissions import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.CopyActionProcessingStream -import org.gradle.api.internal.file.copy.FileCopyDetailsInternal import org.gradle.api.logging.Logging import org.gradle.api.tasks.WorkResult import org.gradle.api.tasks.WorkResults @@ -95,18 +92,6 @@ public open class ShadowCopyAction( } } - public abstract class BaseStreamAction : CopyActionProcessingStreamAction { - override fun processFile(details: FileCopyDetailsInternal) { - if (details.isDirectory) visitDir(details) else visitFile(details) - } - - protected open fun visitDir(dirDetails: FileCopyDetails) {} - protected abstract fun visitFile(fileDetails: FileCopyDetails) - - protected open val FileCopyDetails.isClass: Boolean get() = relativePath.pathString.endsWith(CLASS_SUFFIX) - protected open val FileCopyDetails.isJar: Boolean get() = relativePath.pathString.endsWith(".jar") - } - public open class RelativeArchivePath( public open val entry: ZipEntry, private val preserveFileTimestamps: Boolean, From 0bd4d6201d0dc5e6b447b59ff0f0abd04ef72e5e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Feb 2025 20:36:26 -0500 Subject: [PATCH 256/941] Correct inheritFrom usages in Kotlin DSL (#1261) * Tweak the sample code about using inheritFrom * Test `canInheritFromOtherManifest` --- src/docs/configuration/README.md | 6 ++-- .../gradle/plugins/shadow/JavaPluginTest.kt | 29 +++++++++++++++++++ 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/docs/configuration/README.md b/src/docs/configuration/README.md index 94660b90a..2b4f202a5 100644 --- a/src/docs/configuration/README.md +++ b/src/docs/configuration/README.md @@ -89,15 +89,13 @@ If it is desired to inherit a manifest from a JAR task other than the standard ` on the `shadowJar.manifest` object can be used to configure the upstream. ```groovy -tasks.register('testJar', Jar) { +def testJar = tasks.register('testJar', Jar) { manifest { attributes 'Description': 'This is an application JAR' } } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - manifest { - inheritFrom(project.tasks.testJar.manifest) - } + manifest.inheritFrom(testJar.get().manifest) } ``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 69bef1588..00b3a1e8b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -619,4 +619,33 @@ class JavaPluginTest : BasePluginTest() { ) } } + + @Test + fun canInheritFromOtherManifest() { + projectScriptPath.appendText( + """ + jar { + manifest { + attributes 'Foo-Attr': 'Foo-Value' + } + } + def testJar = tasks.register('testJar', Jar) { + manifest { + attributes 'Bar-Attr': 'Bar-Value' + } + } + $shadowJar { + manifest.inheritFrom(testJar.get().manifest) + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + transform { it.manifest.mainAttributes }.isNotEmpty() + getMainAttr("Foo-Attr").isEqualTo("Foo-Value") + getMainAttr("Bar-Attr").isEqualTo("Bar-Value") + } + } } From cfa109c4b2c74cd9dfe7d157e2587c4efbc2e967 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Feb 2025 21:01:38 -0500 Subject: [PATCH 257/941] Tweak manifest attr size checks (#1262) `Jar.manifest.mainAttributes` should never be empty. --- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 7 ++++--- .../github/jengelman/gradle/plugins/shadow/util/JarPath.kt | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 00b3a1e8b..8422753a1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -4,6 +4,7 @@ import assertk.all import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEqualTo +import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isNotNull import assertk.assertions.isNull @@ -209,7 +210,7 @@ class JavaPluginTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - transform { it.manifest.mainAttributes }.isNotEmpty() + transform { it.mainAttrSize }.isGreaterThan(1) getMainAttr(multiReleaseAttributeKey).isEqualTo("true") } } @@ -481,7 +482,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { transform { actual -> actual.entries().toList().map { it.name }.filter { it.endsWith(".class") } } .single().isEqualTo("my/plugin/MyPlugin.class") - transform { it.manifest.mainAttributes }.isNotEmpty() + transform { it.mainAttrSize }.isGreaterThan(0) // Doesn't contain Gradle classes. getMainAttr(classPathAttributeKey).isNull() @@ -643,7 +644,7 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - transform { it.manifest.mainAttributes }.isNotEmpty() + transform { it.mainAttrSize }.isGreaterThan(2) getMainAttr("Foo-Attr").isEqualTo("Foo-Value") getMainAttr("Bar-Attr").isEqualTo("Bar-Value") } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index bf79c670a..143be5525 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -17,6 +17,8 @@ class JarPath(val path: Path) : JarFile(path.toFile()), Path by path { + val mainAttrSize: Int get() = manifest.mainAttributes.size + fun getMainAttr(name: String): String? { return manifest.mainAttributes.getValue(name) } From 4fe57086b02e6e24b9f610aceeb5c4ae88713dcf Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Feb 2025 21:05:04 -0500 Subject: [PATCH 258/941] Suppress default method warnings for JarPath (#1263) * Try to override defaults * Revert "Try to override defaults" This reverts commit d9b7374315c8d54870128f560eab54774fec9856. * Suppress `JavaDefaultMethodsNotOverriddenByDelegation` --- .../com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index 143be5525..ffd80d546 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -13,6 +13,7 @@ import java.util.zip.ZipFile * We must declare some functions like [kotlin.io.path.deleteExisting] explicitly as they could not * be delegated to [JarPath] type. */ +@Suppress("JavaDefaultMethodsNotOverriddenByDelegation") class JarPath(val path: Path) : JarFile(path.toFile()), Path by path { From 6ff7ad90db4756e4760ad2269c493dd065cbd60f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 18 Feb 2025 01:19:00 -0500 Subject: [PATCH 259/941] Drop ShadowStats and related usages (#1264) * Remove `ShadowStats` * Mark `RelocateClassContext` and `RelocatePathContext` as value classes * Update changelog --- api/shadow.api | 102 ++++-------------- src/docs/changes/README.md | 1 + .../gradle/plugins/shadow/ShadowStats.kt | 72 ------------- .../shadow/internal/RealStreamAction.kt | 7 +- .../shadow/internal/RelocatorRemapper.kt | 6 +- .../shadow/relocation/RelocateClassContext.kt | 24 +---- .../shadow/relocation/RelocatePathContext.kt | 24 +---- .../shadow/relocation/SimpleRelocator.kt | 2 - .../plugins/shadow/tasks/ShadowCopyAction.kt | 3 - .../gradle/plugins/shadow/tasks/ShadowJar.kt | 6 -- .../ComponentsXmlResourceTransformer.kt | 3 +- .../Log4j2PluginsCacheFileTransformer.kt | 8 +- .../transformers/ServiceFileTransformer.kt | 4 +- .../shadow/transformers/TransformerContext.kt | 5 - .../transformers/BaseTransformerTest.kt | 2 - .../PropertiesFileTransformerTest.kt | 2 +- .../ServiceFileTransformerTest.kt | 2 +- 17 files changed, 40 insertions(+), 233 deletions(-) delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt diff --git a/api/shadow.api b/api/shadow.api index bb7bc55f2..e800309cc 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -64,32 +64,6 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowPlugin : public fun apply (Lorg/gradle/api/Project;)V } -public class com/github/jengelman/gradle/plugins/shadow/ShadowStats { - public fun ()V - public fun finishJar ()V - public fun getAverageTimePerJar ()D - public fun getAverageTimeSecsPerJar ()D - public fun getBuildScanData ()Ljava/util/Map; - public fun getJarCount ()I - public fun getJarEndTime ()J - public fun getJarStartTime ()J - public fun getJarTiming ()J - public fun getProcessingJar ()Z - public fun getRelocationString ()Ljava/lang/String; - public fun getRelocations ()Ljava/util/Map; - public fun getTotalTime ()J - public fun getTotalTimeSecs ()D - public fun printStats ()V - public fun relocate (Ljava/lang/String;Ljava/lang/String;)V - public fun setJarCount (I)V - public fun setJarEndTime (J)V - public fun setJarStartTime (J)V - public fun setProcessingJar (Z)V - public fun setTotalTime (J)V - public fun startJar ()V - public fun toString ()Ljava/lang/String; -} - public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter : java/io/Serializable { public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; @@ -115,59 +89,31 @@ public abstract interface annotation class com/github/jengelman/gradle/plugins/s } public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext { - public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Companion; - public fun (Ljava/lang/String;)V - public fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V - public synthetic fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder; - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; - public final fun copy (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; - public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; + public static final synthetic fun box-impl (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; + public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String; public fun equals (Ljava/lang/Object;)Z + public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z + public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z public final fun getClassName ()Ljava/lang/String; - public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; public fun hashCode ()I + public static fun hashCode-impl (Ljava/lang/String;)I public fun toString ()Ljava/lang/String; -} - -public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder { - public fun ()V - public final fun build ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; - public final fun className (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder; - public final fun stats (Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder; -} - -public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Companion { - public final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext$Builder; + public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String; + public final synthetic fun unbox-impl ()Ljava/lang/String; } public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext { - public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Companion; - public fun (Ljava/lang/String;)V - public fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V - public synthetic fun (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder; - public final fun component1 ()Ljava/lang/String; - public final fun component2 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; - public final fun copy (Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; - public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;Ljava/lang/String;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; + public static final synthetic fun box-impl (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; + public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String; public fun equals (Ljava/lang/Object;)Z + public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z + public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z public final fun getPath ()Ljava/lang/String; - public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; public fun hashCode ()I + public static fun hashCode-impl (Ljava/lang/String;)I public fun toString ()Ljava/lang/String; -} - -public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder { - public fun ()V - public final fun build ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; - public final fun path (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder; - public final fun stats (Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder; -} - -public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Companion { - public final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext$Builder; + public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String; + public final synthetic fun unbox-impl ()Ljava/lang/String; } public abstract interface class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { @@ -175,8 +121,8 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/reloc public abstract fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; public abstract fun canRelocateClass (Ljava/lang/String;)Z public abstract fun canRelocatePath (Ljava/lang/String;)Z - public abstract fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; - public abstract fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; + public abstract fun relocateClass-XBGRxQs (Ljava/lang/String;)Ljava/lang/String; + public abstract fun relocatePath-bvWaKNU (Ljava/lang/String;)Ljava/lang/String; } public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator$Companion { @@ -198,8 +144,8 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public final fun getExcludes ()Lorg/gradle/api/provider/SetProperty; public final fun getIncludes ()Lorg/gradle/api/provider/SetProperty; public fun include (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; - public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; - public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; + public fun relocateClass-XBGRxQs (Ljava/lang/String;)Ljava/lang/String; + public fun relocatePath-bvWaKNU (Ljava/lang/String;)Ljava/lang/String; } public class com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest : com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest, org/gradle/api/java/archives/Manifest { @@ -227,7 +173,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; - public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lorg/gradle/api/tasks/util/PatternSet;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ZLjava/util/Set;)V + public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lorg/gradle/api/tasks/util/PatternSet;ZLjava/util/Set;)V public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; } @@ -565,20 +511,17 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion; public fun (Ljava/lang/String;Ljava/io/InputStream;)V public fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;)V - public fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)V - public synthetic fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public static final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; public final fun component1 ()Ljava/lang/String; public final fun component2 ()Ljava/io/InputStream; public final fun component3 ()Ljava/util/Set; - public final fun component4 ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; - public final fun copy (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; - public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public final fun copy (Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;Ljava/lang/String;Ljava/io/InputStream;Ljava/util/Set;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext; public fun equals (Ljava/lang/Object;)Z public final fun getInputStream ()Ljava/io/InputStream; public final fun getPath ()Ljava/lang/String; public final fun getRelocators ()Ljava/util/Set; - public final fun getStats ()Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -589,7 +532,6 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans public final fun inputStream (Ljava/io/InputStream;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; public final fun path (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; public final fun relocators (Ljava/util/Set;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; - public final fun stats (Lcom/github/jengelman/gradle/plugins/shadow/ShadowStats;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; } public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion { diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 2c713a3be..0d6e55b54 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -14,6 +14,7 @@ **Removed** - **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) +- **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) ## [v9.0.0-beta8] (2025-02-08) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt deleted file mode 100644 index 45406acb7..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowStats.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import org.gradle.api.GradleException - -public open class ShadowStats { - public open var totalTime: Long = 0 - public open var jarStartTime: Long = 0 - public open var jarEndTime: Long = 0 - public open var jarCount: Int = 1 - public open var processingJar: Boolean = false - public open val relocations: MutableMap = mutableMapOf() - - public open val relocationString: String - get() { - return relocations.map { (k, v) -> "$k → $v" } - .sorted() - .joinToString("\n") - } - - public open val jarTiming: Long - get() = jarEndTime - jarStartTime - - public open val totalTimeSecs: Double - get() = totalTime / 1000.0 - - public open val averageTimePerJar: Double - get() = totalTime / jarCount.toDouble() - - public open val averageTimeSecsPerJar: Double - get() = averageTimePerJar / 1000.0 - - public open val buildScanData: Map - get() = mapOf( - "dependencies" to jarCount.toString(), - "relocations" to relocationString, - ) - - public open fun relocate(src: String, dst: String) { - relocations[src] = dst - } - - public open fun startJar() { - if (processingJar) throw GradleException("Can only time one entry at a time") - processingJar = true - jarStartTime = System.currentTimeMillis() - } - - public open fun finishJar() { - if (processingJar) { - jarEndTime = System.currentTimeMillis() - jarCount++ - totalTime += jarTiming - processingJar = false - } - } - - public open fun printStats() { - println(this) - } - - override fun toString(): String { - return """ - ******************* - GRADLE SHADOW STATS - - Total Jars: $jarCount (includes project) - Total Time: ${totalTimeSecs}s [${totalTime}ms] - Average Time/Jar: ${averageTimeSecsPerJar}s [${averageTimePerJar}ms] - ******************* - """.trimIndent() - } -} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt index 8fb034bb1..c7fb90fe6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.internal -import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.ArchiveFileTreeElement import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.RelativeArchivePath @@ -34,12 +33,11 @@ internal class RealStreamAction( private val relocators: Set, private val patternSet: PatternSet, private val unusedClasses: Set, - private val stats: ShadowStats, private val zipFile: File, private val preserveFileTimestamps: Boolean, private val logger: Logger, ) : CopyActionProcessingStreamAction { - private val remapper = RelocatorRemapper(relocators, stats) + private val remapper = RelocatorRemapper(relocators) private val visitedFiles = mutableSetOf() init { @@ -100,7 +98,6 @@ internal class RealStreamAction( } private fun processArchive(fileDetails: FileCopyDetails) { - stats.startJar() ZipFile(fileDetails.file).use { archive -> archive.entries.asSequence() .map { @@ -114,7 +111,6 @@ internal class RealStreamAction( } } } - stats.finishJar() } private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { @@ -242,7 +238,6 @@ internal class RealStreamAction( path = mappedPath, inputStream = steam, relocators = relocators, - stats = stats, ), ) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 4e8f59afd..3c2fb20ea 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.internal -import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator @@ -16,7 +15,6 @@ import org.objectweb.asm.commons.Remapper */ internal class RelocatorRemapper( private val relocators: Set, - private val stats: ShadowStats, ) : Remapper() { private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") @@ -44,10 +42,10 @@ internal class RelocatorRemapper( for (relocator in relocators) { if (relocator.canRelocateClass(newName)) { - val classContext = RelocateClassContext(className = newName, stats = stats) + val classContext = RelocateClassContext(newName) return prefix + relocator.relocateClass(classContext) + suffix } else if (relocator.canRelocatePath(newName)) { - val pathContext = RelocatePathContext(path = newName, stats = stats) + val pathContext = RelocatePathContext(newName) return prefix + relocator.relocatePath(pathContext) + suffix } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt index b30c1c31d..7a85ec702 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt @@ -1,22 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.relocation -import com.github.jengelman.gradle.plugins.shadow.ShadowStats - -public data class RelocateClassContext @JvmOverloads constructor( - val className: String, - val stats: ShadowStats = ShadowStats(), -) { - public class Builder { - private var className = "" - private var stats = ShadowStats() - - public fun className(className: String): Builder = apply { this.className = className } - public fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } - public fun build(): RelocateClassContext = RelocateClassContext(className, stats) - } - - public companion object { - @JvmStatic - public fun builder(): Builder = Builder() - } -} +@JvmInline +public value class RelocateClassContext( + public val className: String, +) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt index ff8ca3288..7fc499606 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt @@ -1,22 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.relocation -import com.github.jengelman.gradle.plugins.shadow.ShadowStats - -public data class RelocatePathContext @JvmOverloads constructor( - val path: String, - val stats: ShadowStats = ShadowStats(), -) { - public class Builder { - private var path = "" - private var stats = ShadowStats() - - public fun path(path: String): Builder = apply { this.path = path } - public fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } - public fun build(): RelocatePathContext = RelocatePathContext(path, stats) - } - - public companion object { - @JvmStatic - public fun builder(): Builder = Builder() - } -} +@JvmInline +public value class RelocatePathContext( + public val path: String, +) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index f2e9e2931..b3641accd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -115,7 +115,6 @@ public open class SimpleRelocator @JvmOverloads constructor( override fun relocatePath(context: RelocatePathContext): String { val path = context.path - context.stats.relocate(pathPattern, shadedPathPattern) return if (rawString) { path.replace(pathPattern.toRegex(), shadedPathPattern) } else { @@ -124,7 +123,6 @@ public open class SimpleRelocator @JvmOverloads constructor( } override fun relocateClass(context: RelocateClassContext): String { - context.stats.relocate(pathPattern, shadedPathPattern) val clazz = context.className return if (rawString) clazz else clazz.replaceFirst(pattern.toRegex(), shadedPattern) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index a0247a6ed..e8fb652b4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.tasks -import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.RealStreamAction import com.github.jengelman.gradle.plugins.shadow.internal.RealStreamAction.Companion.CLASS_SUFFIX import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor @@ -40,7 +39,6 @@ public open class ShadowCopyAction( private val transformers: Set, private val relocators: Set, private val patternSet: PatternSet, - private val stats: ShadowStats, private val preserveFileTimestamps: Boolean, private val unusedClasses: Set, ) : CopyAction { @@ -62,7 +60,6 @@ public open class ShadowCopyAction( relocators, patternSet, unusedClasses, - stats, zipFile, preserveFileTimestamps, logger, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 27934da76..4172b3cad 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -1,7 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin -import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.DefaultZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter @@ -103,9 +102,6 @@ public abstract class ShadowJar : } } - @get:Internal - internal val stats: ShadowStats = ShadowStats() - @get:Internal protected open val rootPatternSet: PatternSet get() = (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet @@ -268,7 +264,6 @@ public abstract class ShadowJar : from(includedDependencies) injectMultiReleaseAttrIfPresent() super.copy() - logger.info(stats.toString()) } override fun createCopyAction(): CopyAction { @@ -290,7 +285,6 @@ public abstract class ShadowJar : transformers.get(), relocators.get() + packageRelocators, rootPatternSet, - stats, isPreserveFileTimestamps, unusedClasses, ) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index d21bc44e4..458765800 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -105,11 +105,10 @@ public open class ComponentsXmlResourceTransformer : Transformer { public const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" private fun getRelocatedClass(className: String?, context: TransformerContext): String? { - val stats = context.stats if (!className.isNullOrEmpty()) { for (relocator in context.relocators) { if (relocator.canRelocateClass(className)) { - val relocateClassContext = RelocateClassContext(className, stats) + val relocateClassContext = RelocateClassContext(className) return relocator.relocateClass(relocateClassContext) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index 280b7a344..94ce05001 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator @@ -34,7 +33,6 @@ public open class Log4j2PluginsCacheFileTransformer : Transformer { * [Relocator] instances to share across the transformation stages. */ private val tempRelocators = mutableListOf() - private var stats: ShadowStats? = null override fun canTransformResource(element: FileTreeElement): Boolean { return PLUGIN_CACHE_FILE == element.relativePath.pathString @@ -49,10 +47,6 @@ public open class Log4j2PluginsCacheFileTransformer : Transformer { } tempRelocators.addAll(context.relocators) - - if (stats == null) { - stats = context.stats - } } /** @@ -79,7 +73,7 @@ public open class Log4j2PluginsCacheFileTransformer : Transformer { pluginCache.allCategories.values.forEach { currentMap -> currentMap.values.forEach { currentPluginEntry -> val className = currentPluginEntry.className - val relocateClassContext = RelocateClassContext(className, requireNotNull(stats)) + val relocateClassContext = RelocateClassContext(className) tempRelocators.firstOrNull { it.canRelocateClass(className) }?.let { relocator -> // Then we perform that relocation and update the plugin entry to reflect the new value. currentPluginEntry.className = relocator.relocateClass(relocateClassContext) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index d633d9334..d4e00ffe7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -50,7 +50,7 @@ public open class ServiceFileTransformer( var resource = context.path.substringAfter("$path/") context.relocators.forEach { relocator -> if (relocator.canRelocateClass(resource)) { - val classContext = RelocateClassContext(className = resource, stats = context.stats) + val classContext = RelocateClassContext(className = resource) resource = relocator.relocateClass(classContext) return@forEach } @@ -62,7 +62,7 @@ public open class ServiceFileTransformer( var line = it context.relocators.forEach { relocator -> if (relocator.canRelocateClass(line)) { - val lineContext = RelocateClassContext(className = line, stats = context.stats) + val lineContext = RelocateClassContext(line) line = relocator.relocateClass(lineContext) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt index 1ee33cd0c..59c412e18 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import java.io.InputStream @@ -8,23 +7,19 @@ public data class TransformerContext @JvmOverloads constructor( val path: String, val inputStream: InputStream, val relocators: Set = emptySet(), - val stats: ShadowStats = ShadowStats(), ) { public class Builder { private var path = "" private var inputStream: InputStream? = null private var relocators = emptySet() - private var stats = ShadowStats() public fun path(path: String): Builder = apply { this.path = path } public fun inputStream(inputStream: InputStream): Builder = apply { this.inputStream = inputStream } public fun relocators(relocators: Set): Builder = apply { this.relocators = relocators } - public fun stats(stats: ShadowStats): Builder = apply { this.stats = stats } public fun build(): TransformerContext = TransformerContext( path = path, inputStream = inputStream ?: error("inputStream is required"), relocators = relocators, - stats = stats, ) } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 3a963a537..30bd720dd 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import com.github.jengelman.gradle.plugins.shadow.ShadowStats import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create @@ -31,7 +30,6 @@ abstract class BaseTransformerTest { protected companion object { const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" - val sharedStats = ShadowStats() fun Transformer.canTransformResource(path: String): Boolean { val element = createDefaultFileTreeElement(relativePath = RelativePath.parse(true, path)) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index d6f900b98..f6b013f5d 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -161,7 +161,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest, charset: Charset = Charsets.ISO_8859_1): TransformerContext { val properties = Properties().apply { putAll(input) } - return TransformerContext(path, properties.inputStream(charset), stats = sharedStats) + return TransformerContext(path, properties.inputStream(charset)) } @JvmStatic diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index bfbaf67ec..bc86d492b 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -150,7 +150,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() private companion object { fun context(path: String, input: String, vararg relocators: Relocator): TransformerContext { - return TransformerContext(path, input.byteInputStream(), relocators = relocators.toSet(), stats = sharedStats) + return TransformerContext(path, input.byteInputStream(), relocators = relocators.toSet()) } fun ZipFile.getContent(entryName: String): String { From cd0013bd6631c76a6b79d037c441f15aadcde1e4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 18 Feb 2025 05:51:57 -0500 Subject: [PATCH 260/941] Revert "Add BooleanParameterizedTest" (#1266) This reverts commit ad448a53 --- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 6 ++++-- .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 6 ++++-- .../shadow/transformers/AppendingTransformerTest.kt | 9 ++++++--- .../GroovyExtensionModuleTransformerTest.kt | 6 ++++-- .../shadow/transformers/ServiceFileTransformerTest.kt | 9 ++++++--- .../plugins/shadow/util/BooleanParameterizedTest.kt | 10 ---------- 6 files changed, 24 insertions(+), 22 deletions(-) delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/BooleanParameterizedTest.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 8422753a1..1b5760a23 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -16,7 +16,6 @@ import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries @@ -32,6 +31,8 @@ import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledForJreRange import org.junit.jupiter.api.condition.JRE +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class JavaPluginTest : BasePluginTest() { @Test @@ -465,7 +466,8 @@ class JavaPluginTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/459", "https://github.com/GradleUp/shadow/issues/852", ) - @BooleanParameterizedTest + @ParameterizedTest + @ValueSource(booleans = [false, true]) fun excludeGradleApiByDefault(legacy: Boolean) { writeGradlePluginModule(legacy) projectScriptPath.appendText( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 0e787c0ce..64aa16242 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -9,7 +9,6 @@ import assertk.assertions.isEqualTo import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey -import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath @@ -42,6 +41,8 @@ import org.gradle.testkit.runner.BuildResult import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.api.io.TempDir +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class PublishingTest : BasePluginTest() { @TempDir @@ -139,7 +140,8 @@ class PublishingTest : BasePluginTest() { assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven/1.0/maven-1.0.module"))) } - @BooleanParameterizedTest + @ParameterizedTest + @ValueSource(booleans = [false, true]) fun publishShadowedGradlePlugin(legacy: Boolean) { writeGradlePluginModule(legacy) projectScriptPath.appendText( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 499389a03..55cb546d8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -2,12 +2,14 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo -import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class AppendingTransformerTest : BaseTransformerTest() { - @BooleanParameterizedTest + @ParameterizedTest + @ValueSource(booleans = [false, true]) fun appendTestProperties(shortSyntax: Boolean) { val one = buildJarOne { insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) @@ -40,7 +42,8 @@ class AppendingTransformerTest : BaseTransformerTest() { assertThat(content).isEqualTo(CONTENT_ONE_TWO) } - @BooleanParameterizedTest + @ParameterizedTest + @ValueSource(booleans = [false, true]) fun appendApplicationYaml(shortSyntax: Boolean) { val one = buildJarOne { insert("resources/$APPLICATION_YML_FILE", CONTENT_ONE) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index a61c0c541..ff4204135 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -10,14 +10,16 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_VERSION import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR -import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.getContent import java.nio.file.Path import kotlin.io.path.appendText import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { - @BooleanParameterizedTest + @ParameterizedTest + @ValueSource(booleans = [false, true]) fun groovyExtensionModuleTransformer(shortSyntax: Boolean) { val config = if (shortSyntax) { """ diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index b204e1cb6..5b0e538dd 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -2,15 +2,17 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo -import com.github.jengelman.gradle.plugins.shadow.util.BooleanParameterizedTest import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class ServiceFileTransformerTest : BaseTransformerTest() { - @BooleanParameterizedTest + @ParameterizedTest + @ValueSource(booleans = [false, true]) fun serviceResourceTransformer(shortSyntax: Boolean) { val config = if (shortSyntax) { """ @@ -41,7 +43,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } - @BooleanParameterizedTest + @ParameterizedTest + @ValueSource(booleans = [false, true]) fun serviceResourceTransformerAlternatePath(shortSyntax: Boolean) { val one = buildJarOne { insert(ENTRY_FOO_SHADE, CONTENT_ONE) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/BooleanParameterizedTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/BooleanParameterizedTest.kt deleted file mode 100644 index ba3d474f0..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/BooleanParameterizedTest.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.util - -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource - -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) -@Retention(AnnotationRetention.RUNTIME) -@ParameterizedTest -@ValueSource(booleans = [false, true]) -annotation class BooleanParameterizedTest From fe807238007ccad5bff48d1ee404860ab9998b36 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 18 Feb 2025 07:11:35 -0500 Subject: [PATCH 261/941] Minor stuffs (#1267) --- lint-baseline.xml | 48 ++-- src/docs/application-plugin/README.md | 4 +- .../plugins/shadow/ApplicationPluginTest.kt | 4 +- .../gradle/plugins/shadow/ShadowBasePlugin.kt | 1 + .../shadow/internal/RealStreamAction.kt | 256 ------------------ .../plugins/shadow/tasks/ShadowCopyAction.kt | 245 ++++++++++++++++- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 21 +- 7 files changed, 281 insertions(+), 298 deletions(-) delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt diff --git a/lint-baseline.xml b/lint-baseline.xml index d8e6256d5..58a4aaee6 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -37,66 +37,66 @@ + errorLine1="import org.gradle.api.internal.DocumentationRegistry" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.file.CopyActionProcessingStreamAction" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.file.DefaultFilePermissions" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.file.copy.CopyAction" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.file.copy.CopyActionProcessingStream" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + errorLine1="import org.gradle.api.internal.file.copy.FileCopyDetailsInternal" + errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -107,7 +107,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +118,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,7 +129,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +140,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/docs/application-plugin/README.md b/src/docs/application-plugin/README.md index 3d2765d19..a04f90c0f 100644 --- a/src/docs/application-plugin/README.md +++ b/src/docs/application-plugin/README.md @@ -79,8 +79,8 @@ application { // `shadow` is the name of the distribution created by Shadow plugin distributions.named('shadow') { // Optionally, you can add more files into extra directory in the distribution like this: - contents.into('extra') { - from project.file('extra/echo.sh') + contents.from('extra/echo.sh') { + into 'extra' } } ``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index e2f79e7f1..fb9b9021c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -156,8 +156,8 @@ class ApplicationPluginTest : BasePluginTest() { prepare( projectBlock = """ distributions.named('$DISTRIBUTION_NAME') { - contents.into('extra') { - from project.file('extra/echo.sh') + contents.from('extra/echo.sh') { + into 'extra' } } """.trimIndent(), diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 38622b7fc..241b491d0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -14,6 +14,7 @@ public abstract class ShadowBasePlugin : Plugin { if (GradleVersion.current() < GradleVersion.version("8.3")) { throw GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.") } + @Suppress("DEPRECATION") project.extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) project.configurations.create(CONFIGURATION_NAME) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt deleted file mode 100644 index c7fb90fe6..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RealStreamAction.kt +++ /dev/null @@ -1,256 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.ArchiveFileTreeElement -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.RelativeArchivePath -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext -import java.io.File -import java.io.InputStream -import java.util.zip.ZipException -import org.apache.tools.zip.UnixStat -import org.apache.tools.zip.ZipFile -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.GradleException -import org.gradle.api.file.FileCopyDetails -import org.gradle.api.file.FileTreeElement -import org.gradle.api.file.RelativePath -import org.gradle.api.internal.file.CopyActionProcessingStreamAction -import org.gradle.api.internal.file.copy.FileCopyDetailsInternal -import org.gradle.api.logging.Logger -import org.gradle.api.tasks.util.PatternSet -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.commons.ClassRemapper - -/** - * Modified from [org.gradle.api.internal.file.archive.ZipCopyAction.StreamAction](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java#L89). - */ -internal class RealStreamAction( - private val zipOutStr: ZipOutputStream, - encoding: String?, - private val transformers: Set, - private val relocators: Set, - private val patternSet: PatternSet, - private val unusedClasses: Set, - private val zipFile: File, - private val preserveFileTimestamps: Boolean, - private val logger: Logger, -) : CopyActionProcessingStreamAction { - private val remapper = RelocatorRemapper(relocators) - private val visitedFiles = mutableSetOf() - - init { - if (encoding != null) { - this.zipOutStr.setEncoding(encoding) - } - } - - override fun processFile(details: FileCopyDetailsInternal) { - if (details.isDirectory) visitDir(details) else visitFile(details) - } - - private fun visitFile(fileDetails: FileCopyDetails) { - if (fileDetails.isJar) { - processArchive(fileDetails) - } else { - try { - val isClass = fileDetails.isClass - if (!remapper.hasRelocators() || !isClass) { - if (isTransformable(fileDetails)) { - transform(fileDetails) - } else { - val mappedPath = remapper.map(fileDetails.relativePath.pathString) - val entry = zipEntry(mappedPath, preserveFileTimestamps, fileDetails.lastModified) { - unixMode = UnixStat.FILE_FLAG or fileDetails.permissions.toUnixNumeric() - } - zipOutStr.putNextEntry(entry) - fileDetails.copyTo(zipOutStr) - zipOutStr.closeEntry() - } - } else if (isClass && !isUnused(fileDetails.path)) { - remapClass(fileDetails) - } - recordVisit(fileDetails.relativePath) - } catch (e: Exception) { - throw GradleException("Could not add $fileDetails to ZIP '$zipFile'.", e) - } - } - } - - private fun visitDir(dirDetails: FileCopyDetails) { - try { - // Trailing slash in name indicates that entry is a directory. - val path = dirDetails.relativePath.pathString + "/" - val entry = zipEntry(path, preserveFileTimestamps, dirDetails.lastModified) { - unixMode = UnixStat.DIR_FLAG or dirDetails.permissions.toUnixNumeric() - } - zipOutStr.putNextEntry(entry) - zipOutStr.closeEntry() - recordVisit(dirDetails.relativePath) - } catch (e: Exception) { - throw GradleException("Could not add $dirDetails to ZIP '$zipFile'.", e) - } - } - - private fun recordVisit(path: RelativePath): Boolean { - return visitedFiles.add(path.pathString) - } - - private fun processArchive(fileDetails: FileCopyDetails) { - ZipFile(fileDetails.file).use { archive -> - archive.entries.asSequence() - .map { - ArchiveFileTreeElement(RelativeArchivePath(it, preserveFileTimestamps)) - } - .filter { - patternSet.asSpec.isSatisfiedBy(it.asFileTreeElement()) - }.forEach { archiveElement -> - if (archiveElement.relativePath.isFile) { - visitArchiveFile(archiveElement, archive) - } - } - } - } - - private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { - if (recordVisit(archiveDir)) { - zipOutStr.putNextEntry(archiveDir.entry) - zipOutStr.closeEntry() - } - } - - private fun visitArchiveFile(archiveFile: ArchiveFileTreeElement, archive: ZipFile) { - val archiveFilePath = archiveFile.relativePath - if (archiveFile.isClass || !isTransformable(archiveFile)) { - if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { - if (!remapper.hasRelocators() || !archiveFile.isClass) { - copyArchiveEntry(archiveFilePath, archive) - } else { - remapClass(archiveFilePath, archive) - } - } - } else { - transform(archiveFile, archive) - } - } - - private fun addParentDirectories(file: RelativeArchivePath?) { - file?.let { - addParentDirectories(it.parent) - if (!it.isFile) { - visitArchiveDirectory(it) - } - } - } - - private fun isUnused(classPath: String): Boolean { - val classPathWithoutExtension = classPath.substringBeforeLast(".") - val className = classPathWithoutExtension.replace('/', '.') - val result = unusedClasses.contains(className) - if (result) { - logger.debug("Dropping unused class: $className") - } - return result - } - - private fun remapClass(file: RelativeArchivePath, archive: ZipFile) { - if (file.isClass) { - val entry = zipEntry(remapper.mapPath(file) + CLASS_SUFFIX, preserveFileTimestamps) - addParentDirectories(RelativeArchivePath(entry, preserveFileTimestamps)) - remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) - } - } - - private fun remapClass(fileCopyDetails: FileCopyDetails) { - if (fileCopyDetails.isClass) { - fileCopyDetails.file.inputStream().use { - remapClass(it, fileCopyDetails.path, fileCopyDetails.lastModified) - } - } - } - - /** - * Applies remapping to the given class with the specified relocation path. The remapped class is then written - * to the zip file. [classInputStream] is closed automatically to prevent future file leaks. - * See #364 and #408. - */ - private fun remapClass(classInputStream: InputStream, path: String, lastModified: Long) { - // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. - // Copying the original constant pool should be avoided because it would keep references - // to the original class names. This is not a problem at runtime (because these entries in the - // constant pool are never used), but confuses some tools such as Felix's maven-bundle-plugin - // that use the constant pool to determine the dependencies of a class. - val cw = ClassWriter(0) - val cr = ClassReader(classInputStream) - val cv = ClassRemapper(cw, remapper) - - try { - cr.accept(cv, ClassReader.EXPAND_FRAMES) - } catch (t: Throwable) { - throw GradleException("Error in ASM processing class $path", t) - } - - val renamedClass = cw.toByteArray() - // Temporarily remove the multi-release prefix. - val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() - val newPath = path.replace(multiReleasePrefix, "") - val mappedName = multiReleasePrefix + remapper.mapPath(newPath) - try { - // Now we put it back on so the class file is written out with the right extension. - zipOutStr.putNextEntry(zipEntry("$mappedName.class", preserveFileTimestamps, lastModified)) - renamedClass.inputStream().use { - it.copyTo(zipOutStr) - } - zipOutStr.closeEntry() - } catch (_: ZipException) { - logger.warn("We have a duplicate $mappedName in source project") - } - } - - private fun copyArchiveEntry(archiveFile: RelativeArchivePath, archive: ZipFile) { - val mappedPath = remapper.map(archiveFile.entry.name) - val entry = zipEntry(mappedPath, preserveFileTimestamps, archiveFile.entry.time) - val mappedFile = RelativeArchivePath(entry, preserveFileTimestamps) - addParentDirectories(mappedFile) - zipOutStr.putNextEntry(mappedFile.entry) - archive.getInputStream(archiveFile.entry).use { - it.copyTo(zipOutStr) - } - zipOutStr.closeEntry() - } - - private fun transform(element: ArchiveFileTreeElement, archive: ZipFile) { - transformAndClose(element, archive.getInputStream(element.relativePath.entry)) - } - - private fun transform(details: FileCopyDetails) { - transformAndClose(details, details.file.inputStream()) - } - - private fun transformAndClose(element: FileTreeElement, inputStream: InputStream) { - inputStream.use { steam -> - val mappedPath = remapper.map(element.relativePath.pathString) - transformers.find { - it.canTransformResource(element) - }?.transform( - TransformerContext( - path = mappedPath, - inputStream = steam, - relocators = relocators, - ), - ) - } - } - - private fun isTransformable(element: FileTreeElement): Boolean { - return transformers.any { it.canTransformResource(element) } - } - - companion object { - const val CLASS_SUFFIX = ".class" - - private val FileCopyDetails.isClass: Boolean get() = relativePath.pathString.endsWith(CLASS_SUFFIX) - private val FileCopyDetails.isJar: Boolean get() = relativePath.pathString.endsWith(".jar") - } -} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index e8fb652b4..c076b53c5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -1,32 +1,43 @@ package com.github.jengelman.gradle.plugins.shadow.tasks -import com.github.jengelman.gradle.plugins.shadow.internal.RealStreamAction -import com.github.jengelman.gradle.plugins.shadow.internal.RealStreamAction.Companion.CLASS_SUFFIX +import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import java.io.File import java.io.InputStream import java.io.OutputStream import java.util.GregorianCalendar +import java.util.zip.ZipException +import kotlin.sequences.forEach +import org.apache.tools.zip.UnixStat import org.apache.tools.zip.Zip64RequiredException import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipFile import org.apache.tools.zip.ZipOutputStream import org.gradle.api.GradleException +import org.gradle.api.file.FileCopyDetails import org.gradle.api.file.FilePermissions import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath import org.gradle.api.internal.DocumentationRegistry +import org.gradle.api.internal.file.CopyActionProcessingStreamAction import org.gradle.api.internal.file.DefaultFilePermissions import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.CopyActionProcessingStream +import org.gradle.api.internal.file.copy.FileCopyDetailsInternal +import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging import org.gradle.api.tasks.WorkResult import org.gradle.api.tasks.WorkResults import org.gradle.api.tasks.bundling.Zip import org.gradle.api.tasks.util.PatternSet +import org.objectweb.asm.ClassReader +import org.objectweb.asm.ClassWriter +import org.objectweb.asm.commons.ClassRemapper /** * Modified from [org.gradle.api.internal.file.archive.ZipCopyAction.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java). @@ -53,7 +64,7 @@ public open class ShadowCopyAction( try { zipOutStream.use { zos -> stream.process( - RealStreamAction( + StreamAction( zos, encoding, transformers, @@ -146,7 +157,235 @@ public open class ShadowCopyAction( override fun copyTo(file: File): Boolean = throw UnsupportedOperationException() } + private class StreamAction( + private val zipOutStr: ZipOutputStream, + encoding: String?, + private val transformers: Set, + private val relocators: Set, + private val patternSet: PatternSet, + private val unusedClasses: Set, + private val zipFile: File, + private val preserveFileTimestamps: Boolean, + private val logger: Logger, + ) : CopyActionProcessingStreamAction { + private val remapper = RelocatorRemapper(relocators) + private val visitedFiles = mutableSetOf() + + init { + if (encoding != null) { + this.zipOutStr.setEncoding(encoding) + } + } + + override fun processFile(details: FileCopyDetailsInternal) { + if (details.isDirectory) visitDir(details) else visitFile(details) + } + + private fun visitFile(fileDetails: FileCopyDetails) { + if (fileDetails.isJar) { + processArchive(fileDetails) + } else { + try { + val isClass = fileDetails.isClass + if (!remapper.hasRelocators() || !isClass) { + if (isTransformable(fileDetails)) { + transform(fileDetails) + } else { + val mappedPath = remapper.map(fileDetails.relativePath.pathString) + val entry = zipEntry(mappedPath, preserveFileTimestamps, fileDetails.lastModified) { + unixMode = UnixStat.FILE_FLAG or fileDetails.permissions.toUnixNumeric() + } + zipOutStr.putNextEntry(entry) + fileDetails.copyTo(zipOutStr) + zipOutStr.closeEntry() + } + } else if (isClass && !isUnused(fileDetails.path)) { + remapClass(fileDetails) + } + recordVisit(fileDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $fileDetails to ZIP '$zipFile'.", e) + } + } + } + + private fun visitDir(dirDetails: FileCopyDetails) { + try { + // Trailing slash in name indicates that entry is a directory. + val path = dirDetails.relativePath.pathString + "/" + val entry = zipEntry(path, preserveFileTimestamps, dirDetails.lastModified) { + unixMode = UnixStat.DIR_FLAG or dirDetails.permissions.toUnixNumeric() + } + zipOutStr.putNextEntry(entry) + zipOutStr.closeEntry() + recordVisit(dirDetails.relativePath) + } catch (e: Exception) { + throw GradleException("Could not add $dirDetails to ZIP '$zipFile'.", e) + } + } + + private fun recordVisit(path: RelativePath): Boolean { + return visitedFiles.add(path.pathString) + } + + private fun processArchive(fileDetails: FileCopyDetails) { + ZipFile(fileDetails.file).use { archive -> + archive.entries.asSequence() + .map { + ArchiveFileTreeElement(RelativeArchivePath(it, preserveFileTimestamps)) + } + .filter { + patternSet.asSpec.isSatisfiedBy(it.asFileTreeElement()) + }.forEach { archiveElement -> + if (archiveElement.relativePath.isFile) { + visitArchiveFile(archiveElement, archive) + } + } + } + } + + private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { + if (recordVisit(archiveDir)) { + zipOutStr.putNextEntry(archiveDir.entry) + zipOutStr.closeEntry() + } + } + + private fun visitArchiveFile(archiveFile: ArchiveFileTreeElement, archive: ZipFile) { + val archiveFilePath = archiveFile.relativePath + if (archiveFile.isClass || !isTransformable(archiveFile)) { + if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { + if (!remapper.hasRelocators() || !archiveFile.isClass) { + copyArchiveEntry(archiveFilePath, archive) + } else { + remapClass(archiveFilePath, archive) + } + } + } else { + transform(archiveFile, archive) + } + } + + private fun addParentDirectories(file: RelativeArchivePath?) { + file?.let { + addParentDirectories(it.parent) + if (!it.isFile) { + visitArchiveDirectory(it) + } + } + } + + private fun isUnused(classPath: String): Boolean { + val classPathWithoutExtension = classPath.substringBeforeLast(".") + val className = classPathWithoutExtension.replace('/', '.') + val result = unusedClasses.contains(className) + if (result) { + logger.debug("Dropping unused class: $className") + } + return result + } + + private fun remapClass(file: RelativeArchivePath, archive: ZipFile) { + if (file.isClass) { + val entry = zipEntry(remapper.mapPath(file) + CLASS_SUFFIX, preserveFileTimestamps) + addParentDirectories(RelativeArchivePath(entry, preserveFileTimestamps)) + remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) + } + } + + private fun remapClass(fileCopyDetails: FileCopyDetails) { + if (fileCopyDetails.isClass) { + fileCopyDetails.file.inputStream().use { + remapClass(it, fileCopyDetails.path, fileCopyDetails.lastModified) + } + } + } + + /** + * Applies remapping to the given class with the specified relocation path. The remapped class is then written + * to the zip file. [classInputStream] is closed automatically to prevent future file leaks. + * See #364 and #408. + */ + private fun remapClass(classInputStream: InputStream, path: String, lastModified: Long) { + // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. + // Copying the original constant pool should be avoided because it would keep references + // to the original class names. This is not a problem at runtime (because these entries in the + // constant pool are never used), but confuses some tools such as Felix's maven-bundle-plugin + // that use the constant pool to determine the dependencies of a class. + val cw = ClassWriter(0) + val cr = ClassReader(classInputStream) + val cv = ClassRemapper(cw, remapper) + + try { + cr.accept(cv, ClassReader.EXPAND_FRAMES) + } catch (t: Throwable) { + throw GradleException("Error in ASM processing class $path", t) + } + + val renamedClass = cw.toByteArray() + // Temporarily remove the multi-release prefix. + val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() + val newPath = path.replace(multiReleasePrefix, "") + val mappedName = multiReleasePrefix + remapper.mapPath(newPath) + try { + // Now we put it back on so the class file is written out with the right extension. + zipOutStr.putNextEntry(zipEntry("$mappedName.class", preserveFileTimestamps, lastModified)) + renamedClass.inputStream().use { + it.copyTo(zipOutStr) + } + zipOutStr.closeEntry() + } catch (_: ZipException) { + logger.warn("We have a duplicate $mappedName in source project") + } + } + + private fun copyArchiveEntry(archiveFile: RelativeArchivePath, archive: ZipFile) { + val mappedPath = remapper.map(archiveFile.entry.name) + val entry = zipEntry(mappedPath, preserveFileTimestamps, archiveFile.entry.time) + val mappedFile = RelativeArchivePath(entry, preserveFileTimestamps) + addParentDirectories(mappedFile) + zipOutStr.putNextEntry(mappedFile.entry) + archive.getInputStream(archiveFile.entry).use { + it.copyTo(zipOutStr) + } + zipOutStr.closeEntry() + } + + private fun transform(element: ArchiveFileTreeElement, archive: ZipFile) { + transformAndClose(element, archive.getInputStream(element.relativePath.entry)) + } + + private fun transform(details: FileCopyDetails) { + transformAndClose(details, details.file.inputStream()) + } + + private fun transformAndClose(element: FileTreeElement, inputStream: InputStream) { + inputStream.use { steam -> + val mappedPath = remapper.map(element.relativePath.pathString) + transformers.find { + it.canTransformResource(element) + }?.transform( + TransformerContext( + path = mappedPath, + inputStream = steam, + relocators = relocators, + ), + ) + } + } + + private fun isTransformable(element: FileTreeElement): Boolean { + return transformers.any { it.canTransformResource(element) } + } + + companion object { + private val FileCopyDetails.isClass: Boolean get() = relativePath.pathString.endsWith(CLASS_SUFFIX) + private val FileCopyDetails.isJar: Boolean get() = relativePath.pathString.endsWith(".jar") + } + } + public companion object { + private const val CLASS_SUFFIX = ".class" private val logger = Logging.getLogger(ShadowCopyAction::class.java) public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 4172b3cad..857cb4d9e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -320,18 +320,17 @@ public abstract class ShadowJar : private fun injectMultiReleaseAttrIfPresent() { // TODO: https://github.com/GradleUp/shadow/pull/1239#discussion_r1946064032. - val includeMultiReleaseAttr = includedDependencies.files.filter { it.extension == "jar" } - .any { - try { - JarFile(it).use { jarFile -> - // Manifest might be null or the attribute name is invalid, or any other case. - runCatching { jarFile.manifest.mainAttributes.getValue(multiReleaseAttributeKey) }.getOrNull() - } == "true" - } catch (_: IOException) { - // If the jar file is not valid, ignore it. - false - } + val includeMultiReleaseAttr = includedDependencies.files.any { + try { + JarFile(it).use { jarFile -> + // Manifest might be null or the attribute name is invalid, or any other case. + runCatching { jarFile.manifest.mainAttributes.getValue(multiReleaseAttributeKey) }.getOrNull() + } == "true" + } catch (_: IOException) { + // If the jar file is not valid, ignore it. + false } + } if (includeMultiReleaseAttr) { manifest.attributes[multiReleaseAttributeKey] = true } From 87d5aa86daf47176e860afa7ccfc0defc263dff8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Feb 2025 08:16:11 +0800 Subject: [PATCH 262/941] Update plugin com.gradle.develocity to v3.19.2 (#1268) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 0066caee7..6d012d90f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "3.19.1" + id("com.gradle.develocity") version "3.19.2" } develocity { From bd6f0cef956b14928de69a1bf9e0bbe8212e9b4d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 18 Feb 2025 22:13:58 -0500 Subject: [PATCH 263/941] Replace shadow names in functional tests (#1269) * Write test main classes to my packages by default * Replace other shadow packages * Replace groups for artifacts a, b, c, and d * Replace project names --- .../plugins/shadow/ApplicationPluginTest.kt | 16 ++--- .../gradle/plugins/shadow/BasePluginTest.kt | 27 ++++---- .../gradle/plugins/shadow/FilteringTest.kt | 26 +++---- .../gradle/plugins/shadow/JavaPluginTest.kt | 68 +++++++++---------- .../gradle/plugins/shadow/PublishingTest.kt | 46 ++++++------- .../gradle/plugins/shadow/RelocationTest.kt | 15 ++-- .../shadow/caching/RelocationCachingTest.kt | 4 +- .../shadow/caching/ShadowJarCachingTest.kt | 28 ++++---- .../shadow/caching/TransformerCachingTest.kt | 8 +-- .../ServiceFileTransformerTest.kt | 14 ++-- .../shadow/transformers/TransformersTest.kt | 20 +++--- 11 files changed, 136 insertions(+), 136 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index fb9b9021c..da42e4977 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -73,7 +73,7 @@ class ApplicationPluginTest : BasePluginTest() { commonAssertions( jarPath("myapp-shadow/lib/myapp-1.0-all.jar", installPath), - entriesContained = arrayOf("shadow/Main.class", "junit/framework/Test.class"), + entriesContained = arrayOf("my/Main.class", "junit/framework/Test.class"), ) val unixScript = path("myapp-shadow/bin/myapp", installPath) @@ -119,7 +119,7 @@ class ApplicationPluginTest : BasePluginTest() { projectBlock = """ shadowJar { manifest { - attributes '$mainClassAttributeKey': 'shadow.Main2' + attributes '$mainClassAttributeKey': 'my.Main2' } } """.trimIndent(), @@ -135,8 +135,8 @@ class ApplicationPluginTest : BasePluginTest() { assertions(run(runShadowTask).output, "foo") commonAssertions( jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"), - entriesContained = entriesInA + arrayOf("shadow/Main.class", "shadow/Main2.class"), - mainClassAttr = "shadow.Main2", + entriesContained = entriesInA + arrayOf("my/Main.class", "my/Main2.class"), + mainClassAttr = "my.Main2", ) projectScriptPath.appendText( @@ -213,7 +213,7 @@ class ApplicationPluginTest : BasePluginTest() { projectBlock: String = "", applicationBlock: String = "", settingsBlock: String = "", - dependenciesBlock: String = "implementation 'shadow:a:1.0'", + dependenciesBlock: String = "implementation 'my:a:1.0'", runShadowBlock: String = "", ) { writeMainClass(withImports = mainClassWithImports) @@ -222,7 +222,7 @@ class ApplicationPluginTest : BasePluginTest() { apply plugin: 'application' $projectBlock application { - mainClass = 'shadow.Main' + mainClass = 'my.Main' $applicationBlock } dependencies { @@ -244,8 +244,8 @@ class ApplicationPluginTest : BasePluginTest() { private fun commonAssertions( jarPath: JarPath, - entriesContained: Array = entriesInA + "shadow/Main.class", - mainClassAttr: String = "shadow.Main", + entriesContained: Array = entriesInA + "my/Main.class", + mainClassAttr: String = "my.Main", classPathAttr: String? = null, ) { assertThat(jarPath).useAll { 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 cfa754c47..f373f7e83 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 @@ -57,19 +57,19 @@ abstract class BasePluginTest { ) localRepo.module("junit", "junit", "3.8.2") { useJar(junitJar) - }.module("shadow", "a", "1.0") { + }.module("my", "a", "1.0") { buildJar { insert("a.properties", "a") insert("a2.properties", "a2") } - }.module("shadow", "b", "1.0") { + }.module("my", "b", "1.0") { buildJar { insert("b.properties", "b") } }.publish() - artifactAJar = path("shadow/a/1.0/a-1.0.jar", parent = localRepo.root) - artifactBJar = path("shadow/b/1.0/b-1.0.jar", parent = localRepo.root) + artifactAJar = path("my/a/1.0/a-1.0.jar", parent = localRepo.root) + artifactBJar = path("my/b/1.0/b-1.0.jar", parent = localRepo.root) entriesInA = arrayOf("a.properties", "a2.properties") entriesInB = arrayOf("b.properties") entriesInAB = entriesInA + entriesInB @@ -98,7 +98,7 @@ abstract class BasePluginTest { val projectScriptPath: Path get() = path("build.gradle") val settingsScriptPath: Path get() = path("settings.gradle") - open val outputShadowJar: JarPath get() = jarPath("build/libs/shadow-1.0-all.jar") + open val outputShadowJar: JarPath get() = jarPath("build/libs/my-1.0-all.jar") val outputServerShadowJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") fun getDefaultProjectBuildScript( @@ -106,7 +106,7 @@ abstract class BasePluginTest { withGroup: Boolean = false, withVersion: Boolean = false, ): String { - val groupInfo = if (withGroup) "group = 'shadow'" else "" + val groupInfo = if (withGroup) "group = 'my'" else "" val versionInfo = if (withVersion) "version = '1.0'" else "" return """ plugins { @@ -124,7 +124,7 @@ abstract class BasePluginTest { // this test, and we won't accidentally use cached outputs from a different test or a different build. // https://docs.gradle.org/current/userguide/build_cache.html#sec:build_cache_configure_local buildCacheBlock: String = "local { directory = file('build-cache') }", - endBlock: String = "rootProject.name = 'shadow'", + endBlock: String = "rootProject.name = 'my'", ): String { return """ $startBlock @@ -173,31 +173,32 @@ abstract class BasePluginTest { } fun publishArtifactCD(circular: Boolean = false) { - localRepo.module("shadow", "c", "1.0") { + localRepo.module("my", "c", "1.0") { buildJar { insert("c.properties", "c") } if (circular) { - addDependency("shadow", "d", "1.0") + addDependency("my", "d", "1.0") } - }.module("shadow", "d", "1.0") { + }.module("my", "d", "1.0") { buildJar { insert("d.properties", "d") } - addDependency("shadow", "c", "1.0") + addDependency("my", "c", "1.0") }.publish() } fun writeMainClass( sourceSet: String = "main", + packageName: String = "my", withImports: Boolean = false, className: String = "Main", ) { val imports = if (withImports) "import junit.framework.Test;" else "" val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" - path("src/$sourceSet/java/shadow/$className.java").writeText( + path("src/$sourceSet/java/$packageName/$className.java").writeText( """ - package shadow; + package $packageName; $imports public class $className { public static void main(String[] args) { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 442511627..9a025dfe9 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -24,8 +24,8 @@ class FilteringTest : BasePluginTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:a:1.0' - implementation 'shadow:b:1.0' + implementation 'my:a:1.0' + implementation 'my:b:1.0' } """.trimIndent() + System.lineSeparator(), ) @@ -76,11 +76,11 @@ class FilteringTest : BasePluginTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:d:1.0' + implementation 'my:d:1.0' } $shadowJar { dependencies { - exclude(dependency('shadow:d:.*')) + exclude(dependency('my:d:.*')) } } """.trimIndent(), @@ -100,7 +100,7 @@ class FilteringTest : BasePluginTest() { commonAssertions() val replaced = projectScriptPath.readText() - .replace("exclude(dependency('shadow:d:1.0'))", "exclude(dependency('shadow:c:1.0'))") + .replace("exclude(dependency('my:d:1.0'))", "exclude(dependency('my:c:1.0'))") projectScriptPath.writeText(replaced) val result = run(shadowJarTask) @@ -123,7 +123,7 @@ class FilteringTest : BasePluginTest() { commonAssertions() val replaced = projectScriptPath.readText() - .replace("exclude(dependency('shadow:d:1.0'))", "exclude 'a.properties'") + .replace("exclude(dependency('my:d:1.0'))", "exclude 'a.properties'") projectScriptPath.writeText(replaced) val result = run(shadowJarTask) @@ -147,18 +147,18 @@ class FilteringTest : BasePluginTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:d:1.0' + implementation 'my:d:1.0' } $shadowJar { dependencies { - include(dependency('shadow:d:1.0')) + include(dependency('my:d:1.0')) } } """.trimIndent(), ) - path("src/main/java/shadow/Passed.java").writeText( + path("src/main/java/my/Passed.java").writeText( """ - package shadow; + package my; public class Passed {} """.trimIndent(), ) @@ -168,7 +168,7 @@ class FilteringTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( "d.properties", - "shadow/Passed.class", + "my/Passed.class", ) val entries = entriesInAB + "c.properties" doesNotContainEntries(*entries) @@ -260,11 +260,11 @@ class FilteringTest : BasePluginTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:d:1.0' + implementation 'my:d:1.0' } $shadowJar { dependencies { - exclude(dependency('shadow:d:1.0')) + exclude(dependency('my:d:1.0')) } } """.trimIndent(), diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 1b5760a23..e2bd47f74 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -83,7 +83,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - "shadow/Main.class", + "my/Main.class", "junit/framework/Test.class", ) } @@ -98,9 +98,9 @@ class JavaPluginTest : BasePluginTest() { @Test fun includeProjectSources() { - path("src/main/java/shadow/Passed.java").writeText( + path("src/main/java/my/Passed.java").writeText( """ - package shadow; + package my; public class Passed {} """.trimIndent(), ) @@ -111,7 +111,7 @@ class JavaPluginTest : BasePluginTest() { implementation 'junit:junit:3.8.2' } $shadowJar { - archiveBaseName = 'shadow' + archiveBaseName = 'fat' archiveClassifier = '' archiveVersion = '' } @@ -120,9 +120,9 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) - assertThat(jarPath("build/libs/shadow.jar")).useAll { + assertThat(jarPath("build/libs/fat.jar")).useAll { containsEntries( - "shadow/Passed.class", + "my/Passed.class", "junit/framework/Test.class", ) doesNotContainEntries("/") @@ -222,7 +222,7 @@ class JavaPluginTest : BasePluginTest() { ) @Test fun excludeSomeMetaInfFilesByDefault() { - localRepo.module("shadow", "a", "1.0") { + localRepo.module("my", "a", "1.0") { buildJar { insert("a.properties", "a") insert("META-INF/INDEX.LIST", "JarIndex-Version: 1.0") @@ -236,16 +236,16 @@ class JavaPluginTest : BasePluginTest() { } }.publish() - path("src/main/java/shadow/Passed.java").writeText( + path("src/main/java/my/Passed.java").writeText( """ - package shadow; + package my; public class Passed {} """.trimIndent(), ) projectScriptPath.appendText( """ dependencies { - implementation 'shadow:a:1.0' + implementation 'my:a:1.0' } """.trimIndent(), ) @@ -254,7 +254,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - "shadow/Passed.class", + "my/Passed.class", "a.properties", "META-INF/a.properties", ) @@ -275,8 +275,8 @@ class JavaPluginTest : BasePluginTest() { projectScriptPath.appendText( """ dependencies { - runtimeOnly 'shadow:a:1.0' - shadow 'shadow:b:1.0' + runtimeOnly 'my:a:1.0' + shadow 'my:b:1.0' } """.trimIndent(), ) @@ -291,20 +291,20 @@ class JavaPluginTest : BasePluginTest() { @Test fun includeJavaLibraryConfigurationsByDefault() { - localRepo.module("shadow", "api", "1.0") { + localRepo.module("my", "api", "1.0") { buildJar { insert("api.properties", "api") } - }.module("shadow", "implementation-dep", "1.0") { + }.module("my", "implementation-dep", "1.0") { buildJar { insert("implementation-dep.properties", "implementation-dep") } - }.module("shadow", "implementation", "1.0") { + }.module("my", "implementation", "1.0") { buildJar { insert("implementation.properties", "implementation") } - addDependency("shadow", "implementation-dep", "1.0") - }.module("shadow", "runtimeOnly", "1.0") { + addDependency("my", "implementation-dep", "1.0") + }.module("my", "runtimeOnly", "1.0") { buildJar { insert("runtimeOnly.properties", "runtimeOnly") } @@ -314,9 +314,9 @@ class JavaPluginTest : BasePluginTest() { """ ${getDefaultProjectBuildScript("java-library", withGroup = true, withVersion = true)} dependencies { - api 'shadow:api:1.0' - implementation 'shadow:implementation:1.0' - runtimeOnly 'shadow:runtimeOnly:1.0' + api 'my:api:1.0' + implementation 'my:implementation:1.0' + runtimeOnly 'my:runtimeOnly:1.0' } """.trimIndent(), ) @@ -338,8 +338,8 @@ class JavaPluginTest : BasePluginTest() { projectScriptPath.appendText( """ dependencies { - runtimeOnly 'shadow:a:1.0' - compileOnly 'shadow:b:1.0' + runtimeOnly 'my:a:1.0' + compileOnly 'my:b:1.0' } """.trimIndent(), ) @@ -354,11 +354,11 @@ class JavaPluginTest : BasePluginTest() { @Test fun defaultCopyingStrategy() { - localRepo.module("shadow", "a", "1.0") { + localRepo.module("my", "a", "1.0") { buildJar { insert("META-INF/MANIFEST.MF", "MANIFEST A") } - }.module("shadow", "b", "1.0") { + }.module("my", "b", "1.0") { buildJar { insert("META-INF/MANIFEST.MF", "MANIFEST B") } @@ -367,8 +367,8 @@ class JavaPluginTest : BasePluginTest() { projectScriptPath.appendText( """ dependencies { - runtimeOnly 'shadow:a:1.0' - runtimeOnly 'shadow:b:1.0' + runtimeOnly 'my:a:1.0' + runtimeOnly 'my:b:1.0' } """.trimIndent(), ) @@ -473,8 +473,8 @@ class JavaPluginTest : BasePluginTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:a:1.0' - compileOnly 'shadow:b:1.0' + implementation 'my:a:1.0' + compileOnly 'my:b:1.0' } """.trimIndent(), ) @@ -512,7 +512,7 @@ class JavaPluginTest : BasePluginTest() { from sourceSets.test.output configurations = [project.configurations.testRuntimeClasspath] manifest { - attributes '$mainClassAttributeKey': 'shadow.Main' + attributes '$mainClassAttributeKey': 'my.Main' } } """.trimIndent(), @@ -521,15 +521,15 @@ class JavaPluginTest : BasePluginTest() { val result = run(testShadowJarTask) assertThat(result).taskOutcomeEquals(":$testShadowJarTask", SUCCESS) - assertThat(jarPath("build/libs/shadow-1.0-tests.jar")).useAll { + assertThat(jarPath("build/libs/my-1.0-tests.jar")).useAll { containsEntries( "junit/framework/Test.class", - "shadow/Main.class", + "my/Main.class", ) getMainAttr(mainClassAttributeKey).isNotNull() } - val pathString = path("build/libs/shadow-1.0-tests.jar").toString() + val pathString = path("build/libs/my-1.0-tests.jar").toString() val runningOutput = runProcess("java", "-jar", pathString, "foo") assertThat(runningOutput).contains( "Hello, World! (foo) from Main", @@ -617,7 +617,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(jarPath("build/libs/my-shadow.tar")).useAll { containsEntries( - "shadow/Main.class", + "my/Main.class", "junit/framework/Test.class", ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 64aa16242..a068fc424 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -68,9 +68,9 @@ class PublishingTest : BasePluginTest() { publish() val assertions = { variantAttrs: Array>? -> - assertShadowJarCommon(repoJarPath("shadow/maven-all/1.0/maven-all-1.0.jar")) - assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) - val gmm = gmmAdapter.fromJson(repoPath("shadow/maven-all/1.0/maven-all-1.0.module")) + assertShadowJarCommon(repoJarPath("my/maven-all/1.0/maven-all-1.0.jar")) + assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) + val gmm = gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")) if (variantAttrs == null) { assertShadowVariantCommon(gmm) } else { @@ -135,9 +135,9 @@ class PublishingTest : BasePluginTest() { publish() - assertShadowJarCommon(repoJarPath("shadow/maven/1.0/maven-1.0.jar")) - assertPomCommon(repoPath("shadow/maven/1.0/maven-1.0.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven/1.0/maven-1.0.module"))) + assertShadowJarCommon(repoJarPath("my/maven/1.0/maven-1.0.jar")) + assertPomCommon(repoPath("my/maven/1.0/maven-1.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my/maven/1.0/maven-1.0.module"))) } @ParameterizedTest @@ -195,9 +195,9 @@ class PublishingTest : BasePluginTest() { publish() - assertShadowJarCommon(repoJarPath("shadow/maven-all/1.0/maven-all-1.0-my-classifier.my-ext")) - assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven-all/1.0/maven-all-1.0.module"))) + assertShadowJarCommon(repoJarPath("my/maven-all/1.0/maven-all-1.0-my-classifier.my-ext")) + assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module"))) } @Test @@ -213,7 +213,7 @@ class PublishingTest : BasePluginTest() { apply plugin: 'java' apply plugin: 'maven-publish' version = '1.0' - group = 'shadow' + group = 'my' } """.trimIndent(), ) @@ -241,7 +241,7 @@ class PublishingTest : BasePluginTest() { publish() - assertThat(repoJarPath("shadow/maven-all/1.0/maven-all-1.0.jar")).useAll { + assertThat(repoJarPath("my/maven-all/1.0/maven-all-1.0.jar")).useAll { containsEntries( "aa.properties", "aa2.properties", @@ -249,8 +249,8 @@ class PublishingTest : BasePluginTest() { val entries = entriesInAB + "bb.properties" doesNotContainEntries(*entries) } - assertPomCommon(repoPath("shadow/maven-all/1.0/maven-all-1.0.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("shadow/maven-all/1.0/maven-all-1.0.module"))) + assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module"))) } @Test @@ -312,9 +312,9 @@ class PublishingTest : BasePluginTest() { version = '1.0' """.trimIndent(), dependenciesBlock = """ - implementation 'shadow:a:1.0' - implementation 'shadow:b:1.0' - shadow 'shadow:b:1.0' + implementation 'my:a:1.0' + implementation 'my:b:1.0' + shadow 'my:b:1.0' """.trimIndent(), publicationsBlock = """ java(MavenPublication) { @@ -337,7 +337,7 @@ class PublishingTest : BasePluginTest() { containsEntries(*entriesInAB) } - assertPomCommon(repoPath("com/acme/maven/1.0/maven-1.0.pom"), arrayOf("shadow:a:1.0", "shadow:b:1.0")) + assertPomCommon(repoPath("com/acme/maven/1.0/maven-1.0.pom"), arrayOf("my:a:1.0", "my:b:1.0")) gmmAdapter.fromJson(repoPath("com/acme/maven/1.0/maven-1.0.module")).let { gmm -> // apiElements, runtimeElements, shadowRuntimeElements assertThat(gmm.variantNames).containsOnly( @@ -360,8 +360,8 @@ class PublishingTest : BasePluginTest() { Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, ) transform { it.gavs }.containsOnly( - "shadow:a:1.0", - "shadow:b:1.0", + "my:a:1.0", + "my:b:1.0", ) } assertShadowVariantCommon(gmm) @@ -391,8 +391,8 @@ class PublishingTest : BasePluginTest() { private fun publishConfiguration( projectBlock: String = "", dependenciesBlock: String = """ - implementation 'shadow:a:1.0' - shadow 'shadow:b:1.0' + implementation 'my:a:1.0' + shadow 'my:b:1.0' """.trimIndent(), shadowBlock: String = "", publicationsBlock: String = """ @@ -435,7 +435,7 @@ class PublishingTest : BasePluginTest() { private fun assertPomCommon( pomPath: Path, - gavs: Array = arrayOf("shadow:b:1.0"), + gavs: Array = arrayOf("my:b:1.0"), ) { assertThat(pomReader.read(pomPath).gavs).containsOnly(*gavs) } @@ -443,7 +443,7 @@ class PublishingTest : BasePluginTest() { private fun assertShadowVariantCommon( gmm: GradleModuleMetadata, variantAttrs: Array> = shadowVariantAttrs, - gavs: Array = arrayOf("shadow:b:1.0"), + gavs: Array = arrayOf("my:b:1.0"), body: Assert.() -> Unit = {}, ) { assertThat(gmm.shadowRuntimeElementsVariant).all { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index fa1f6f380..1d9ecf681 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -182,14 +182,14 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - path("src/main/java/shadow/ShadowTest.java").writeText( + path("src/main/java/my/MyTest.java").writeText( """ - package shadow; + package my; import junit.framework.Test; import junit.framework.TestResult; - public class ShadowTest implements Test { + public class MyTest implements Test { public int countTestCases() { return 0; } public void run(TestResult result) { } } @@ -200,7 +200,7 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - "shadow/ShadowTest.class", + "my/MyTest.class", "shadow/junit/Test.class", "shadow/junit", ) @@ -215,7 +215,7 @@ class RelocationTest : BasePluginTest() { assertFailure { // check that the class can be loaded. If the file was not relocated properly, we should get a NoDefClassFound // Isolated class loader with only the JVM system jars and the output jar from the test project - classLoader.loadClass("shadow.ShadowTest") + classLoader.loadClass("my.MyTest") fail("Should not reach here.") }.isInstanceOf(AssertionFailedError::class) } @@ -296,7 +296,7 @@ class RelocationTest : BasePluginTest() { ) @Test fun relocateResourceFiles() { - localRepo.module("shadow", "dep", "1.0") { + localRepo.module("my", "dep", "1.0") { buildJar { insert("foo/dep.properties", "c") } @@ -304,7 +304,6 @@ class RelocationTest : BasePluginTest() { path("src/main/java/foo/Foo.java").writeText( """ package foo; - class Foo {} """.trimIndent(), ) @@ -313,7 +312,7 @@ class RelocationTest : BasePluginTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:dep:1.0' + implementation 'my:dep:1.0' } $shadowJar { relocate 'foo', 'bar' diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index 3204dda47..a37338c20 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -22,7 +22,7 @@ class RelocationCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - "shadow/Main.class", + "my/Main.class", "junit/framework/Test.class", ) } @@ -37,7 +37,7 @@ class RelocationCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - "shadow/Main.class", + "my/Main.class", "foo/junit/framework/Test.class", ) doesNotContainEntries( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index fa2172025..a6143bf95 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -77,14 +77,14 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:a:1.0' - implementation 'shadow:b:1.0' + implementation 'my:a:1.0' + implementation 'my:b:1.0' } """.trimIndent() + System.lineSeparator(), ) assertCompositeExecutions { - val entries = entriesInAB + arrayOf("shadow/Main.class", "shadow/Main2.class") + val entries = entriesInAB + arrayOf("my/Main.class", "my/Main2.class") containsEntries(*entries) } @@ -98,8 +98,8 @@ class ShadowJarCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - "shadow/Main.class", - "shadow/Main2.class", + "my/Main.class", + "my/Main2.class", ) doesNotContainEntries(*entriesInAB) } @@ -107,17 +107,17 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - include 'shadow/Main.class' + include 'my/Main.class' } """.trimIndent() + System.lineSeparator(), ) assertCompositeExecutions { containsEntries( - "shadow/Main.class", + "my/Main.class", ) doesNotContainEntries( - "shadow/Main2.class", + "my/Main2.class", "a.properties", "a2.properties", "b.properties", @@ -127,15 +127,15 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - include 'shadow/Main2.class' + include 'my/Main2.class' } """.trimIndent() + System.lineSeparator(), ) assertCompositeExecutions { containsEntries( - "shadow/Main.class", - "shadow/Main2.class", + "my/Main.class", + "my/Main2.class", ) doesNotContainEntries(*entriesInAB) } @@ -154,7 +154,7 @@ class ShadowJarCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - "shadow/Main.class", + "my/Main.class", "junit/framework/Test.class", ) } @@ -171,7 +171,7 @@ class ShadowJarCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - "shadow/Main.class", + "my/Main.class", ) doesNotContainEntries( "junit/framework/Test.class", @@ -185,7 +185,7 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:d:1.0' + implementation 'my:d:1.0' } """.trimIndent() + System.lineSeparator(), ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 245bb1d04..63b1b4ad0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -39,7 +39,7 @@ class TransformerCachingTest : BaseCachingTest() { fun shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer() { val assertions = { assertCompositeExecutions { - containsEntries("shadow/Main.class") + containsEntries("my/Main.class") } } @@ -66,7 +66,7 @@ class TransformerCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.properties").writeText("foo=bar") val assertions = { name: String -> assertCompositeExecutions { - containsEntries("shadow/Main.class", "foo/$name.properties") + containsEntries("my/Main.class", "foo/$name.properties") getContent("foo/$name.properties").isEqualTo("foo=$name") } } @@ -96,7 +96,7 @@ class TransformerCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.xml").writeText("bar") val assertions = { name: String -> assertCompositeExecutions { - containsEntries("shadow/Main.class", "foo/$name.xml") + containsEntries("my/Main.class", "foo/$name.xml") getContent("foo/$name.xml").contains("$name") } } @@ -167,7 +167,7 @@ class TransformerCachingTest : BaseCachingTest() { } val assertions = { assertCompositeExecutions { - containsEntries("shadow/Main.class") + containsEntries("my/Main.class") } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 5b0e538dd..d2ec31524 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -161,20 +161,20 @@ class ServiceFileTransformerTest : BaseTransformerTest() { ) @Test fun applyTransformersToProjectResources() { - val servicesShadowEntry = "META-INF/services/shadow.Shadow" + val servicesBarEntry = "META-INF/services/foo.Bar" val one = buildJarOne { - insert(servicesShadowEntry, CONTENT_ONE) + insert(servicesBarEntry, CONTENT_ONE) }.toUri().toURL().path - localRepo.module("shadow", "two", "1.0") { + localRepo.module("foo", "bar", "1.0") { buildJar { - insert(servicesShadowEntry, CONTENT_TWO) + insert(servicesBarEntry, CONTENT_TWO) } }.publish() projectScriptPath.appendText( """ dependencies { - implementation 'shadow:two:1.0' + implementation 'foo:bar:1.0' implementation files('$one') } $shadowJar { @@ -182,11 +182,11 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } """.trimIndent(), ) - path("src/main/resources/$servicesShadowEntry").writeText(CONTENT_THREE) + path("src/main/resources/$servicesBarEntry").writeText(CONTENT_THREE) run(shadowJarTask) - val content = outputShadowJar.use { it.getContent(servicesShadowEntry) } + val content = outputShadowJar.use { it.getContent(servicesBarEntry) } assertThat(content).isEqualTo(CONTENT_THREE + "\n" + CONTENT_ONE_TWO) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 6019be919..19a57cecb 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -28,7 +28,7 @@ class TransformersTest : BaseTransformerTest() { """ jar { manifest { - attributes '$mainClassAttributeKey': 'shadow.Main' + attributes '$mainClassAttributeKey': 'my.Main' attributes '$TEST_ENTRY_ATTR_KEY': 'PASSED' } } @@ -39,7 +39,7 @@ class TransformersTest : BaseTransformerTest() { commonAssertions { assertThat(getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("PASSED") - assertThat(getValue(mainClassAttributeKey)).isEqualTo("shadow.Main") + assertThat(getValue(mainClassAttributeKey)).isEqualTo("my.Main") } } @@ -66,10 +66,10 @@ class TransformersTest : BaseTransformerTest() { commonAssertions() - val mf = jarPath("build/libs/shadow-1.0.jar").use { it.manifest } + val mf = jarPath("build/libs/my-1.0.jar").use { it.manifest } assertThat(mf).isNotNull() assertThat(mf.mainAttributes.getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("FAILED") - assertThat(mf.mainAttributes.getValue(mainClassAttributeKey)).isEqualTo("shadow.Main") + assertThat(mf.mainAttributes.getValue(mainClassAttributeKey)).isEqualTo("my.Main") assertThat(mf.mainAttributes.getValue(NEW_ENTRY_ATTR_KEY)).isNull() } @@ -108,8 +108,8 @@ class TransformersTest : BaseTransformerTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:a:1.0' - implementation 'shadow:b:1.0' + implementation 'my:a:1.0' + implementation 'my:b:1.0' } $shadowJar { // Use NoOpTransformer to mock a custom transformer here. @@ -135,8 +135,8 @@ class TransformersTest : BaseTransformerTest() { projectScriptPath.appendText( """ dependencies { - implementation 'shadow:a:1.0' - implementation 'shadow:b:1.0' + implementation 'my:a:1.0' + implementation 'my:b:1.0' } $shadowJar { transform(${transformer.java.name}) $configuration @@ -154,7 +154,7 @@ class TransformersTest : BaseTransformerTest() { private fun commonAssertions( mainAttributesBlock: Attributes.() -> Unit = { assertThat(getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("PASSED") - assertThat(getValue(mainClassAttributeKey)).isEqualTo("shadow.Main") + assertThat(getValue(mainClassAttributeKey)).isEqualTo("my.Main") assertThat(getValue(NEW_ENTRY_ATTR_KEY)).isEqualTo("NEW") }, ) { @@ -170,7 +170,7 @@ class TransformersTest : BaseTransformerTest() { val MANIFEST_ATTRS = """ jar { manifest { - attributes '$mainClassAttributeKey': 'shadow.Main' + attributes '$mainClassAttributeKey': 'my.Main' attributes '$TEST_ENTRY_ATTR_KEY': 'FAILED' } } From 26e6d4dd3c5580f0612e20055d4f15911756ffc6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 19 Feb 2025 01:46:42 -0500 Subject: [PATCH 264/941] Improve RelocationTest and junit entry checks (#1270) * Improve `defaultEnableRelocation` * Improve `relocateDependencyFiles` * Improve `relocateDependencyFilesWithFiltering` * Improve `remapClassNamesForRelocatedFilesInProjectSource` * Improve `relocateDoesNotDropDependencyResources` * Improve `doesNotErrorOnRelocatingJava9Classes` * Reuse `junitEntries` read from the real jar --- .../plugins/shadow/ApplicationPluginTest.kt | 2 +- .../gradle/plugins/shadow/BasePluginTest.kt | 8 +- .../gradle/plugins/shadow/FilteringTest.kt | 4 +- .../gradle/plugins/shadow/JavaPluginTest.kt | 18 +- .../gradle/plugins/shadow/MinimizeTest.kt | 8 +- .../gradle/plugins/shadow/RelocationTest.kt | 184 +++++++++--------- .../shadow/caching/MinimizationCachingTest.kt | 4 +- .../shadow/caching/RelocationCachingTest.kt | 8 +- .../shadow/caching/ShadowJarCachingTest.kt | 4 +- 9 files changed, 120 insertions(+), 120 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index da42e4977..89b55aa7b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -73,7 +73,7 @@ class ApplicationPluginTest : BasePluginTest() { commonAssertions( jarPath("myapp-shadow/lib/myapp-1.0-all.jar", installPath), - entriesContained = arrayOf("my/Main.class", "junit/framework/Test.class"), + entriesContained = arrayOf("my/Main.class", *junitEntries), ) val unixScript = path("myapp-shadow/bin/myapp", installPath) 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 f373f7e83..23b5c6670 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 @@ -193,7 +193,7 @@ abstract class BasePluginTest { packageName: String = "my", withImports: Boolean = false, className: String = "Main", - ) { + ): 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( @@ -212,6 +212,7 @@ abstract class BasePluginTest { } """.trimIndent(), ) + return packageName.replace('.', '/') + "/$className.class" } fun writeClientAndServerModules( @@ -347,6 +348,11 @@ abstract class BasePluginTest { } val junitJar: Path = requireResourceAsPath("junit-3.8.2.jar") + val junitEntries: Array = JarPath(junitJar) + .use { it.entries().toList() } + .map { entry -> entry.name } + .filterNot { it == "junit3.8.2/" || it.startsWith("META-INF/") } + .toTypedArray() val shadowJar: String = """ tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name}) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 9a025dfe9..bb3d2f5e7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -190,7 +190,7 @@ class FilteringTest : BasePluginTest() { assertThat(outputServerShadowJar).useAll { containsEntries( "server/Server.class", - "junit/framework/Test.class", + *junitEntries, ) doesNotContainEntries( "client/Client.class", @@ -216,7 +216,7 @@ class FilteringTest : BasePluginTest() { "server/Server.class", ) doesNotContainEntries( - "junit/framework/Test.class", + *junitEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index e2bd47f74..4294a3d2e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -84,7 +84,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( "my/Main.class", - "junit/framework/Test.class", + *junitEntries, ) } } @@ -123,7 +123,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(jarPath("build/libs/fat.jar")).useAll { containsEntries( "my/Passed.class", - "junit/framework/Test.class", + *junitEntries, ) doesNotContainEntries("/") } @@ -139,7 +139,7 @@ class JavaPluginTest : BasePluginTest() { containsEntries( "client/Client.class", "server/Server.class", - "junit/framework/Test.class", + *junitEntries, ) } } @@ -156,7 +156,7 @@ class JavaPluginTest : BasePluginTest() { ) doesNotContainEntries( "client/Client.class", - "junit/framework/Test.class", + *junitEntries, "client/junit/framework/Test.class", ) } @@ -171,17 +171,19 @@ class JavaPluginTest : BasePluginTest() { @Test fun shadowProjectShadowJar() { writeClientAndServerModules(clientShadowed = true) + val shadowedEntries = junitEntries + .map { it.replace("junit/framework/", "client/junit/framework/") }.toTypedArray() run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { containsEntries( "client/Client.class", - "client/junit/framework/Test.class", "server/Server.class", + *shadowedEntries, ) doesNotContainEntries( - "junit/framework/Test.class", + *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), ) } assertThat(jarPath("client/build/libs/client-1.0-all.jar")).useAll { @@ -523,8 +525,8 @@ class JavaPluginTest : BasePluginTest() { assertThat(result).taskOutcomeEquals(":$testShadowJarTask", SUCCESS) assertThat(jarPath("build/libs/my-1.0-tests.jar")).useAll { containsEntries( - "junit/framework/Test.class", "my/Main.class", + *junitEntries, ) getMainAttr(mainClassAttributeKey).isNotNull() } @@ -618,7 +620,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(jarPath("build/libs/my-shadow.tar")).useAll { containsEntries( "my/Main.class", - "junit/framework/Test.class", + *junitEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index d77b07180..727651018 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -63,7 +63,7 @@ class MinimizeTest : BasePluginTest() { "lib/UnusedLibEntity.class", ) doesNotContainEntries( - "junit/framework/Test.class", + *junitEntries, ) } } @@ -123,7 +123,7 @@ class MinimizeTest : BasePluginTest() { assertThat(outputServerShadowJar).useAll { containsEntries( "server/Server.class", - "junit/framework/Test.class", + *junitEntries, ) doesNotContainEntries( "client/Client.class", @@ -184,7 +184,7 @@ class MinimizeTest : BasePluginTest() { containsEntries( "client/Client.class", "server/Server.class", - "junit/framework/TestCase.class", + *junitEntries, ) } @@ -200,7 +200,7 @@ class MinimizeTest : BasePluginTest() { containsEntries( "client/Client.class", "server/Server.class", - "junit/framework/TestCase.class", + *junitEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 1d9ecf681..84bfdd39e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -2,23 +2,27 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertFailure import assertk.assertThat -import assertk.assertions.isEqualTo +import assertk.assertions.isEmpty import assertk.assertions.isInstanceOf +import assertk.assertions.isNotEmpty import assertk.fail import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries -import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import java.net.URLClassLoader import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource import org.opentest4j.AssertionFailedError class RelocationTest : BasePluginTest() { - @Test - fun defaultEnableRelocation() { + @ParameterizedTest + @MethodSource("prefixProvider") + fun autoRelocation(relocationPrefix: String) { + val mainClass = writeMainClass() projectScriptPath.appendText( """ dependencies { @@ -26,30 +30,22 @@ class RelocationTest : BasePluginTest() { } $shadowJar { enableRelocation = true + relocationPrefix = '$relocationPrefix' } """.trimIndent(), ) + val entryPrefix = relocationPrefix.replace('.', '/') run(shadowJarTask) assertThat(outputShadowJar).useAll { containsEntries( - "META-INF/MANIFEST.MF", - "shadow/junit/textui/ResultPrinter.class", - "shadow/junit/textui/TestRunner.class", - "shadow/junit/framework/Assert.class", - "shadow/junit/framework/AssertionFailedError.class", - "shadow/junit/framework/ComparisonCompactor.class", - "shadow/junit/framework/ComparisonFailure.class", - "shadow/junit/framework/Protectable.class", - "shadow/junit/framework/Test.class", - "shadow/junit/framework/TestCase.class", - "shadow/junit/framework/TestFailure.class", - "shadow/junit/framework/TestListener.class", - "shadow/junit/framework/TestResult$1.class", - "shadow/junit/framework/TestResult.class", - "shadow/junit/framework/TestSuite$1.class", - "shadow/junit/framework/TestSuite.class", + mainClass, + *junitEntries.map { "$entryPrefix/$it" }.toTypedArray(), + ) + doesNotContainEntries( + "$entryPrefix/$mainClass", + *junitEntries, ) } } @@ -59,73 +55,56 @@ class RelocationTest : BasePluginTest() { ) @Test fun relocateDependencyFiles() { + val mainClass = writeMainClass() projectScriptPath.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } $shadowJar { - relocate 'junit.textui', 'a' + relocate 'junit.runner', 'a' relocate 'junit.framework', 'b' - manifest { - attributes 'TEST-VALUE': 'FOO' - } } """.trimIndent(), ) + val runnerFilter = { it: String -> it.startsWith("junit/runner/") } + val frameworkFilter = { it: String -> it.startsWith("junit/framework/") } + val runnerEntries = junitEntries + .filter(runnerFilter) + .map { it.replace("junit/runner/", "a/") }.toTypedArray() + val frameworkEntries = junitEntries + .filter(frameworkFilter) + .map { it.replace("junit/framework/", "b/") }.toTypedArray() + val otherJunitEntries = junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() run(shadowJarTask) assertThat(outputShadowJar).useAll { containsEntries( - "META-INF/MANIFEST.MF", - "a/ResultPrinter.class", - "a/TestRunner.class", - "b/Assert.class", - "b/AssertionFailedError.class", - "b/ComparisonCompactor.class", - "b/ComparisonFailure.class", - "b/Protectable.class", - "b/Test.class", - "b/TestCase.class", - "b/TestFailure.class", - "b/TestListener.class", - "b/TestResult\$1.class", - "b/TestResult.class", - "b/TestSuite\$1.class", - "b/TestSuite.class", + mainClass, + *runnerEntries, + *frameworkEntries, + *otherJunitEntries, ) doesNotContainEntries( - "junit/textui/ResultPrinter.class", - "junit/textui/TestRunner.class", - "junit/framework/Assert.class", - "junit/framework/AssertionFailedError.class", - "junit/framework/ComparisonCompactor.class", - "junit/framework/ComparisonFailure.class", - "junit/framework/Protectable.class", - "junit/framework/Test.class", - "junit/framework/TestCase.class", - "junit/framework/TestFailure.class", - "junit/framework/TestListener.class", - "junit/framework/TestResult\$1.class", - "junit/framework/TestResult.class", - "junit/framework/TestSuite\$1.class", - "junit/framework/TestSuite.class", + *junitEntries.filter { it !in otherJunitEntries }.toTypedArray(), + *otherJunitEntries.map { "a/$it" }.toTypedArray(), + *otherJunitEntries.map { "b/$it" }.toTypedArray(), ) - getMainAttr("TEST-VALUE").isEqualTo("FOO") } } @Test fun relocateDependencyFilesWithFiltering() { + val mainClass = writeMainClass() projectScriptPath.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } $shadowJar { - relocate('junit.textui', 'a') { - exclude 'junit.textui.TestRunner' + relocate('junit.runner', 'a') { + exclude 'junit.runner.BaseTestRunner' } relocate('junit.framework', 'b') { include 'junit.framework.Test*' @@ -133,34 +112,28 @@ class RelocationTest : BasePluginTest() { } """.trimIndent(), ) + val runnerFilter = { it: String -> it.startsWith("junit/runner/") && it != "junit/runner/BaseTestRunner.class" } + val frameworkFilter = { it: String -> it.startsWith("junit/framework/Test") } + val runnerEntries = junitEntries + .filter(runnerFilter) + .map { it.replace("junit/runner/", "a/") }.toTypedArray() + val frameworkEntries = junitEntries + .filter(frameworkFilter) + .map { it.replace("junit/framework/", "b/") }.toTypedArray() + val otherJunitEntries = junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() run(shadowJarTask) assertThat(outputShadowJar).useAll { containsEntries( - "a/ResultPrinter.class", - "b/Test.class", - "b/TestCase.class", - "b/TestFailure.class", - "b/TestListener.class", - "b/TestResult\$1.class", - "b/TestResult.class", - "b/TestSuite\$1.class", - "b/TestSuite.class", - "junit/textui/TestRunner.class", - "junit/framework/Assert.class", - "junit/framework/AssertionFailedError.class", - "junit/framework/ComparisonCompactor.class", - "junit/framework/ComparisonFailure.class", - "junit/framework/Protectable.class", + mainClass, + *runnerEntries, + *frameworkEntries, + *otherJunitEntries, ) doesNotContainEntries( - "a/TestRunner.class", - "b/Assert.class", - "b/AssertionFailedError.class", - "b/ComparisonCompactor.class", - "b/ComparisonFailure.class", - "b/Protectable.class", + *otherJunitEntries.map { "a/$it" }.toTypedArray(), + *otherJunitEntries.map { "b/$it" }.toTypedArray(), ) } } @@ -181,14 +154,14 @@ class RelocationTest : BasePluginTest() { } """.trimIndent(), ) + val shadowedEntries = junitEntries + .map { it.replace("junit/framework/", "shadow/junit/") }.toTypedArray() path("src/main/java/my/MyTest.java").writeText( """ package my; - import junit.framework.Test; import junit.framework.TestResult; - public class MyTest implements Test { public int countTestCases() { return 0; } public void run(TestResult result) { } @@ -201,12 +174,10 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( "my/MyTest.class", - "shadow/junit/Test.class", - "shadow/junit", + *shadowedEntries, ) doesNotContainEntries( - "junit/framework", - "junit/framework/Test.class", + *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), ) } @@ -223,6 +194,11 @@ class RelocationTest : BasePluginTest() { @Test fun relocateDoesNotDropDependencyResources() { + settingsScriptPath.appendText( + """ + include 'core', 'app' + """.trimIndent(), + ) path("core/build.gradle").writeText( """ plugins { @@ -239,9 +215,7 @@ class RelocationTest : BasePluginTest() { path("core/src/main/java/core/Core.java").writeText( """ package core; - import junit.framework.Test; - public class Core {} """.trimIndent(), ) @@ -262,19 +236,13 @@ class RelocationTest : BasePluginTest() { path("app/src/main/java/app/App.java").writeText( """ package app; - import core.Core; import junit.framework.Test; - public class App {} """.trimIndent(), ) - - settingsScriptPath.appendText( - """ - include 'core', 'app' - """.trimIndent(), - ) + val shadowedEntries = junitEntries + .map { it.replace("junit/framework/", "app/junit/framework/") }.toTypedArray() run(":app:$SHADOW_JAR_TASK_NAME") @@ -285,7 +253,10 @@ class RelocationTest : BasePluginTest() { "test.properties", "app/core/Core.class", "app/App.class", - "app/junit/framework/Test.class", + *shadowedEntries, + ) + doesNotContainEntries( + *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), ) } } @@ -358,6 +329,25 @@ class RelocationTest : BasePluginTest() { ) run(shadowJarTask) - // No exception should be thrown + + val entries = outputShadowJar.use { it.entries().toList() } + val included = entries.filter { entry -> + entry.name.startsWith("shaded/com/google/protobuf") || entry.name.startsWith("shaded/io/netty") + } + val excluded = entries.filter { entry -> + entry.name.startsWith("com/google/protobuf") || entry.name.startsWith("io/netty") + } + assertThat(included).isNotEmpty() + assertThat(excluded).isEmpty() + } + + private companion object { + @JvmStatic + fun prefixProvider() = listOf( + // The default values. + arrayOf(ShadowBasePlugin.SHADOW), + arrayOf("new.pkg"), + arrayOf("new/path"), + ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt index 70eed34d0..fb72140f1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt @@ -24,8 +24,8 @@ class MinimizationCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( "server/Server.class", - "junit/framework/Test.class", "client/Client.class", + *junitEntries, ) } @@ -42,7 +42,7 @@ class MinimizationCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( "server/Server.class", - "junit/framework/Test.class", + *junitEntries, ) doesNotContainEntries( "client/Client.class", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index a37338c20..fcead73b7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -23,7 +23,7 @@ class RelocationCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( "my/Main.class", - "junit/framework/Test.class", + *junitEntries, ) } @@ -34,14 +34,16 @@ class RelocationCachingTest : BaseCachingTest() { } """.trimIndent(), ) + val shadowedEntries = junitEntries + .map { it.replace("junit/framework/", "foo/junit/framework/") }.toTypedArray() assertCompositeExecutions { containsEntries( "my/Main.class", - "foo/junit/framework/Test.class", + *shadowedEntries, ) doesNotContainEntries( - "junit/framework/Test.class", + *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index a6143bf95..0d2178374 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -155,7 +155,7 @@ class ShadowJarCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( "my/Main.class", - "junit/framework/Test.class", + *junitEntries, ) } @@ -174,7 +174,7 @@ class ShadowJarCachingTest : BaseCachingTest() { "my/Main.class", ) doesNotContainEntries( - "junit/framework/Test.class", + *junitEntries, ) } } From b9503bb5d55c9c6bc695b91ec68b9283308e97c2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 19 Feb 2025 04:18:53 -0500 Subject: [PATCH 265/941] Reduce duplicated SimpleRelocator to improve performance (#1271) * Mark includes and excludes ad MutableSet, so that we can override `equals` and `hashcode`. * Mark `withDebug` for testing * Reduce duplicates for `packageRelocators` * Revert "Mark `withDebug` for testing" This reverts commit 6a16b572d9aee3cacb2367560d0fbb1bc16ee901. * Check relocator count in `autoRelocation` * Remove `ObjectFactory` from ctor * Update changelog * Cleanups --- api/shadow.api | 20 +++++---- src/docs/changes/README.md | 1 + .../gradle/plugins/shadow/RelocationTest.kt | 14 +++++-- .../shadow/relocation/SimpleRelocator.kt | 41 +++++++++++++++---- .../plugins/shadow/tasks/ShadowCopyAction.kt | 1 + .../gradle/plugins/shadow/tasks/ShadowJar.kt | 20 ++++----- .../shadow/relocation/SimpleRelocatorTest.kt | 1 - .../Log4j2PluginsCacheFileTransformerTest.kt | 2 +- .../ServiceFileTransformerTest.kt | 2 +- .../gradle/plugins/shadow/util/Utils.kt | 19 +-------- 10 files changed, 66 insertions(+), 55 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index e800309cc..43f0e775f 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -130,19 +130,21 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat } public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator : com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { - public fun (Lorg/gradle/api/model/ObjectFactory;)V - public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;)V - public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;)V - public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V - public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V - public fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V - public synthetic fun (Lorg/gradle/api/model/ObjectFactory;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun ()V + public fun (Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/String;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; public fun canRelocateClass (Ljava/lang/String;)Z public fun canRelocatePath (Ljava/lang/String;)Z + public fun equals (Ljava/lang/Object;)Z public fun exclude (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; - public final fun getExcludes ()Lorg/gradle/api/provider/SetProperty; - public final fun getIncludes ()Lorg/gradle/api/provider/SetProperty; + public final fun getExcludes ()Ljava/util/Set; + public final fun getIncludes ()Ljava/util/Set; + public fun hashCode ()I public fun include (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; public fun relocateClass-XBGRxQs (Ljava/lang/String;)Ljava/lang/String; public fun relocatePath-bvWaKNU (Ljava/lang/String;)Ljava/lang/String; diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 0d6e55b54..1bb7d5d6e 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -10,6 +10,7 @@ **Changed** - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) +- Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) **Removed** diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 84bfdd39e..af1f6d2d2 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertFailure import assertk.assertThat +import assertk.assertions.contains import assertk.assertions.isEmpty import assertk.assertions.isInstanceOf import assertk.assertions.isNotEmpty @@ -15,6 +16,7 @@ import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource import org.opentest4j.AssertionFailedError @@ -36,7 +38,7 @@ class RelocationTest : BasePluginTest() { ) val entryPrefix = relocationPrefix.replace('.', '/') - run(shadowJarTask) + val result = run(shadowJarTask, "--info") assertThat(outputShadowJar).useAll { containsEntries( @@ -48,6 +50,10 @@ class RelocationTest : BasePluginTest() { *junitEntries, ) } + // Make sure the relocator count is aligned with the number of unique packages in junit jar. + assertThat(result.output).contains( + "Relocator count: 6.", + ) } @Issue( @@ -345,9 +351,9 @@ class RelocationTest : BasePluginTest() { @JvmStatic fun prefixProvider() = listOf( // The default values. - arrayOf(ShadowBasePlugin.SHADOW), - arrayOf("new.pkg"), - arrayOf("new/path"), + Arguments.of(ShadowBasePlugin.SHADOW), + Arguments.of("new.pkg"), + Arguments.of("new/path"), ) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index b3641accd..f8e39c9ea 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -1,10 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation -import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import java.util.regex.Pattern import org.codehaus.plexus.util.SelectorUtils -import org.gradle.api.model.ObjectFactory -import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input /** @@ -16,7 +13,6 @@ import org.gradle.api.tasks.Input */ @CacheableRelocator public open class SimpleRelocator @JvmOverloads constructor( - objectFactory: ObjectFactory, pattern: String? = null, shadedPattern: String? = null, includes: List? = null, @@ -31,10 +27,10 @@ public open class SimpleRelocator @JvmOverloads constructor( private val sourcePathExcludes = mutableSetOf() @get:Input - public val includes: SetProperty = objectFactory.setProperty() + public val includes: MutableSet = mutableSetOf() @get:Input - public val excludes: SetProperty = objectFactory.setProperty() + public val excludes: MutableSet = mutableSetOf() init { if (rawString) { @@ -71,7 +67,7 @@ public open class SimpleRelocator @JvmOverloads constructor( if (!rawString) { // Create exclude pattern sets for sources. - for (exclude in this.excludes.get()) { + for (exclude in this.excludes) { // Excludes should be subpackages of the global pattern. if (exclude.startsWith(this.pattern)) { sourcePackageExcludes.add( @@ -136,12 +132,39 @@ public open class SimpleRelocator @JvmOverloads constructor( return shadeSourceWithExcludes(content, pathPattern, shadedPathPattern, sourcePathExcludes) } + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is SimpleRelocator) return false + return rawString == other.rawString && + pattern == other.pattern && + pathPattern == other.pathPattern && + shadedPattern == other.shadedPattern && + shadedPathPattern == other.shadedPathPattern && + sourcePackageExcludes == other.sourcePackageExcludes && + sourcePathExcludes == other.sourcePathExcludes && + includes == other.includes && + excludes == other.excludes + } + + override fun hashCode(): Int { + var result = rawString.hashCode() + result = 31 * result + pattern.hashCode() + result = 31 * result + pathPattern.hashCode() + result = 31 * result + shadedPattern.hashCode() + result = 31 * result + shadedPathPattern.hashCode() + result = 31 * result + sourcePackageExcludes.hashCode() + result = 31 * result + sourcePathExcludes.hashCode() + result = 31 * result + includes.hashCode() + result = 31 * result + excludes.hashCode() + return result + } + private fun isIncluded(path: String): Boolean { - return includes.get().isEmpty() || includes.get().any { SelectorUtils.matchPath(it, path, "/", true) } + return includes.isEmpty() || includes.any { SelectorUtils.matchPath(it, path, "/", true) } } private fun isExcluded(path: String): Boolean { - return excludes.get().any { SelectorUtils.matchPath(it, path, "/", true) } + return excludes.any { SelectorUtils.matchPath(it, path, "/", true) } } private companion object { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index c076b53c5..e1056f001 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -172,6 +172,7 @@ public open class ShadowCopyAction( private val visitedFiles = mutableSetOf() init { + logger.info("Relocator count: ${relocators.size}.") if (encoding != null) { this.zipOutStr.setEncoding(encoding) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 857cb4d9e..1656e37a1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -30,7 +30,6 @@ import org.gradle.api.Action import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DuplicatesStrategy -import org.gradle.api.file.FileCollection import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.copy.CopyAction @@ -242,7 +241,7 @@ public abstract class ShadowJar : destination: String, action: Action?, ): ShadowJar = apply { - val relocator = SimpleRelocator(objectFactory, pattern, destination) + val relocator = SimpleRelocator(pattern, destination) addRelocator(relocator, action) } @@ -303,17 +302,14 @@ public abstract class ShadowJar : private val packageRelocators: List get() { if (!enableRelocation.get()) return emptyList() - val prefix = relocationPrefix.get() - // Must cast configurations to Set to fix type mismatch in runtime. - return (configurations.get() as Set).flatMap { configuration -> - configuration.files.flatMap { file -> - JarFile(file).use { jarFile -> - jarFile.entries().toList() - .filter { it.name.endsWith(".class") && it.name != "module-info.class" } - .map { it.name.substringBeforeLast('/').replace('/', '.') } - .map { SimpleRelocator(objectFactory, it, "$prefix.$it") } - } + return includedDependencies.files.flatMap { file -> + JarFile(file).use { jarFile -> + jarFile.entries().toList() + .filter { it.name.endsWith(".class") && it.name != "module-info.class" } + .map { it.name.substringBeforeLast('/').replace('/', '.') } + .toSet() + .map { SimpleRelocator(it, "$prefix.$it") } } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 367ab4476..645740243 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -4,7 +4,6 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.util.SimpleRelocator import org.junit.jupiter.api.Test /** diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index 0a5d3c975..b3744aade 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -10,7 +10,7 @@ import assertk.assertions.startsWith import assertk.fail import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.util.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import com.github.jengelman.gradle.plugins.shadow.util.zipOutputStream import java.io.ByteArrayOutputStream import java.net.URI diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index bc86d492b..1b59c60be 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -5,7 +5,7 @@ import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isTrue import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.util.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import com.github.jengelman.gradle.plugins.shadow.util.zipOutputStream import java.io.InputStream import java.nio.file.Path diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt index ab142567f..638e5b09e 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.util -import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import java.io.OutputStream import org.apache.tools.zip.ZipOutputStream import org.gradle.api.model.ObjectFactory @@ -8,22 +7,6 @@ import org.gradle.testfixtures.ProjectBuilder val testObjectFactory: ObjectFactory = ProjectBuilder.builder().build().objects -@Suppress("TestFunctionName") -fun SimpleRelocator( - pattern: String? = null, - shadedPattern: String? = null, - includes: List = emptyList(), - excludes: List = emptyList(), - rawString: Boolean = false, -): SimpleRelocator = SimpleRelocator( - objectFactory = testObjectFactory, - pattern = pattern, - shadedPattern = shadedPattern, - includes = includes, - excludes = excludes, - rawString = rawString, -) - fun OutputStream.zipOutputStream(): ZipOutputStream { - return if (this is ZipOutputStream) this else ZipOutputStream(this) + return this as? ZipOutputStream ?: ZipOutputStream(this) } From 6a802862a2aaaa0584584c0d92c6fc3c472813eb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 21 Feb 2025 03:57:30 -0500 Subject: [PATCH 266/941] Move DependencyFilter into tasks package (#1272) It should be a public API. --- api/shadow.api | 22 +++++++++---------- src/docs/changes/README.md | 1 + .../internal/AbstractDependencyFilter.kt | 1 + .../{internal => tasks}/DependencyFilter.kt | 2 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 2 +- .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 2 +- 6 files changed, 16 insertions(+), 14 deletions(-) rename src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/{internal => tasks}/DependencyFilter.kt (96%) diff --git a/api/shadow.api b/api/shadow.api index 43f0e775f..e09eec777 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -64,17 +64,6 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowPlugin : public fun apply (Lorg/gradle/api/Project;)V } -public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter : java/io/Serializable { - public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; - public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; - public abstract fun exclude (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter; - public abstract fun include (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter; - public abstract fun project (Ljava/lang/String;)Lorg/gradle/api/specs/Spec; - public abstract fun project (Ljava/util/Map;)Lorg/gradle/api/specs/Spec; - public abstract fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; - public abstract fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; -} - public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor { public abstract fun createArchiveOutputStream (Ljava/io/File;)Lorg/apache/tools/zip/ZipOutputStream; } @@ -168,6 +157,17 @@ public class com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritMani public fun writeTo (Ljava/lang/Object;)Lorg/gradle/api/java/archives/Manifest; } +public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter : java/io/Serializable { + public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; + public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; + public abstract fun exclude (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter; + public abstract fun include (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter; + public abstract fun project (Ljava/lang/String;)Lorg/gradle/api/specs/Spec; + public abstract fun project (Ljava/util/Map;)Lorg/gradle/api/specs/Spec; + public abstract fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; + public abstract fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; +} + public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest : org/gradle/api/java/archives/Manifest { public abstract fun inheritFrom ([Ljava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 1bb7d5d6e..791f2cbe5 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -11,6 +11,7 @@ - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) - Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) +- **BREAKING CHANGE:** Move `DependencyFilter` from `com.github.jengelman.gradle.plugins.shadow.internal` into `com.github.jengelman.gradle.plugins.shadow.tasks`. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) **Removed** diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt index cf4c946db..06f905d56 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt similarity index 96% rename from src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index 213c831a2..f12ada8ea 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -1,4 +1,4 @@ -package com.github.jengelman.gradle.plugins.shadow.internal +package com.github.jengelman.gradle.plugins.shadow.tasks import java.io.Serializable import org.gradle.api.artifacts.Configuration diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 1656e37a1..8f7e59a2a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.DefaultZipCompressor -import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor @@ -15,6 +14,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.sourceSets import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt index b652bde3d..b68176ac4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -1,8 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.tasks -import com.github.jengelman.gradle.plugins.shadow.internal.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import java.lang.reflect.InvocationTargetException From 46f6b3619ad4d1ed174acb10c1c76e72766cbd31 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2025 23:07:46 +0800 Subject: [PATCH 267/941] Update dependency org.junit:junit-bom to v5.12.0 (#1275) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6fcc3ada7..7f847f636 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,7 +27,7 @@ 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. ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" -junit-bom = "org.junit:junit-bom:5.11.4" +junit-bom = "org.junit:junit-bom:5.12.0" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From da34a3ea5760d3b94bf9f7b82a4e645b3c77f193 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 24 Feb 2025 05:46:06 -0500 Subject: [PATCH 268/941] Refactor file visiting logic in StreamAction (#1233) * Remove ShadowJar.rootPatternSet Seems it's unused now. * Test `duplicatesStrategyFailShouldFailBuildForDuplicatedEntries` * Convert the resolution result to ZipTree * Cleanups * Reduce RelativeArchivePath and ArchiveFileTreeElement * Defer includedZipTrees * Fix `failBuildIfProcessingBadJar` * Cleanups * Remove recordVisit * Test more strategies in ServiceFileTransformerTest * Test `shadowJarIsCachedCorrectlyAfterDuplicatesStrategyChanged` * Update baseline * Refactor and simplify visitFile * Show how to add extra files into shadowed jars * Unify from and into orders * Update changelog * Remove TODO for `injectMultiReleaseAttrIfPresent` * Fix merge * Merge `RealStreamAction` back into `ShadowCopyAction` * Cleanups * Adjust ShadowCopyAction ctor * Seems we have no need to close context.inputStream in transformers * Fix `canAddExtraFilesIntoShadowJar` * Mark `withDebug` for testing * Revert "Mark `withDebug` for testing" This reverts commit ec3df52a51e36bc77a289648170b9a71b1bda99d. * Cleanups * Drop `ZipCompressor` * Drop `rootPatternSet` property * Shorten obtaining relative paths > Returns the path of this file, relative to the root of the containing file tree. Always uses '/' as the hierarchy separator, regardless of platform file separator. Same as calling getRelativePath().getPathString(). * Fix merge * Fix merge * Cleanups * Add dir entries in the last --- api/shadow.api | 39 +-- lint-baseline.xml | 82 +---- src/docs/changes/README.md | 7 + src/docs/configuration/README.md | 15 + .../gradle/plugins/shadow/JavaPluginTest.kt | 42 ++- .../shadow/caching/ShadowJarCachingTest.kt | 20 ++ .../ServiceFileTransformerTest.kt | 82 ++++- .../shadow/internal/DefaultZipCompressor.kt | 27 -- .../shadow/internal/RelocatorRemapper.kt | 15 +- .../gradle/plugins/shadow/internal/Utils.kt | 39 +-- .../plugins/shadow/internal/ZipCompressor.kt | 20 -- .../plugins/shadow/tasks/ShadowCopyAction.kt | 326 +++++------------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 49 +-- .../ApacheLicenseResourceTransformer.kt | 2 +- .../ApacheNoticeResourceTransformer.kt | 2 +- .../transformers/AppendingTransformer.kt | 12 +- .../ComponentsXmlResourceTransformer.kt | 2 +- .../DontIncludeResourceTransformer.kt | 3 +- .../GroovyExtensionModuleTransformer.kt | 2 +- .../Log4j2PluginsCacheFileTransformer.kt | 8 +- .../ManifestAppenderTransformer.kt | 10 +- .../ManifestResourceTransformer.kt | 3 +- .../transformers/PropertiesFileTransformer.kt | 2 +- .../transformers/ServiceFileTransformer.kt | 4 +- .../transformers/XmlAppendingTransformer.kt | 2 +- .../shadow/relocation/SimpleRelocatorTest.kt | 10 +- .../transformers/BaseTransformerTest.kt | 11 +- .../gradle/plugins/shadow/util/Utils.kt | 9 + 28 files changed, 334 insertions(+), 511 deletions(-) delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt diff --git a/api/shadow.api b/api/shadow.api index e09eec777..1feac44ae 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -64,10 +64,6 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowPlugin : public fun apply (Lorg/gradle/api/Project;)V } -public abstract interface class com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor { - public abstract fun createArchiveOutputStream (Ljava/io/File;)Lorg/apache/tools/zip/ZipOutputStream; -} - public abstract class com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin : org/gradle/api/Plugin { public fun ()V public synthetic fun apply (Ljava/lang/Object;)V @@ -175,45 +171,14 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; - public fun (Ljava/io/File;Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/lang/String;Ljava/util/Set;Ljava/util/Set;Lorg/gradle/api/tasks/util/PatternSet;ZLjava/util/Set;)V + public fun (Ljava/io/File;Lkotlin/jvm/functions/Function1;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZLjava/lang/String;)V public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; } -public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$ArchiveFileTreeElement : org/gradle/api/file/FileTreeElement { - public fun (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath;)V - public fun asFileTreeElement ()Lorg/gradle/api/file/FileTreeElement; - public fun copyTo (Ljava/io/File;)Z - public fun copyTo (Ljava/io/OutputStream;)V - public fun getFile ()Ljava/io/File; - public fun getLastModified ()J - public fun getMode ()I - public fun getName ()Ljava/lang/String; - public fun getPath ()Ljava/lang/String; - public fun getPermissions ()Lorg/gradle/api/file/FilePermissions; - public fun getRelativePath ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath; - public synthetic fun getRelativePath ()Lorg/gradle/api/file/RelativePath; - public fun getSize ()J - public fun isClass ()Z - public fun isDirectory ()Z - public fun open ()Ljava/io/InputStream; -} - public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion { public final fun getCONSTANT_TIME_FOR_ZIP_ENTRIES ()J } -public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath : org/gradle/api/file/RelativePath { - public fun (Lorg/apache/tools/zip/ZipEntry;Z)V - public final fun charAt (I)C - public fun get (I)C - public fun getEntry ()Lorg/apache/tools/zip/ZipEntry; - public fun getLength ()I - public fun getParent ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$RelativeArchivePath; - public synthetic fun getParent ()Lorg/gradle/api/file/RelativePath; - public fun isClass ()Z - public final fun length ()I -} - public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar : org/gradle/api/tasks/bundling/Jar, com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec { public fun ()V public fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; @@ -231,13 +196,11 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getExcludes ()Ljava/util/Set; public fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getIncludes ()Ljava/util/Set; - protected fun getInternalCompressor ()Lcom/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor; public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; public fun getMinimizeJar ()Lorg/gradle/api/provider/Property; public fun getRelocationPrefix ()Lorg/gradle/api/provider/Property; public fun getRelocators ()Lorg/gradle/api/provider/SetProperty; - protected fun getRootPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; public fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getToMinimize ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getTransformers ()Lorg/gradle/api/provider/SetProperty; diff --git a/lint-baseline.xml b/lint-baseline.xml index 58a4aaee6..ec96ac130 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -41,7 +41,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -52,18 +52,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - - - - @@ -74,7 +63,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -85,7 +74,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -96,7 +85,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -107,7 +96,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +107,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,62 +118,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - - - - - - - - - - - - - - - - - - - - diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 791f2cbe5..81d7b4762 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -12,11 +12,18 @@ - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) - Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) - **BREAKING CHANGE:** Move `DependencyFilter` from `com.github.jengelman.gradle.plugins.shadow.internal` into `com.github.jengelman.gradle.plugins.shadow.tasks`. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) +- Handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + +**Fixed** + +- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) **Removed** - **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) - **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) +- **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) ## [v9.0.0-beta8] (2025-02-08) diff --git a/src/docs/configuration/README.md b/src/docs/configuration/README.md index 2b4f202a5..950514c68 100644 --- a/src/docs/configuration/README.md +++ b/src/docs/configuration/README.md @@ -99,3 +99,18 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow manifest.inheritFrom(testJar.get().manifest) } ``` + +## Adding Extra Files + +The `shadowJar` task is a subclass of the `Jar` task, which means that the +[from](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20groovy.lang.Closure)) +method can be used to add extra files. + +```groovy +tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + from('extra.jar') { + // Copy the contents of the extra.jar file into META-INF/ in the shadowed JAR. + into('META-INF') + } +} +``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 4294a3d2e..4d4521bee 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.all import assertk.assertThat import assertk.assertions.contains +import assertk.assertions.containsMatch import assertk.assertions.isEqualTo import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty @@ -20,8 +21,11 @@ import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr +import com.github.jengelman.gradle.plugins.shadow.util.getStream import com.github.jengelman.gradle.plugins.shadow.util.runProcess import kotlin.io.path.appendText +import kotlin.io.path.name +import kotlin.io.path.outputStream import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin import org.gradle.testfixtures.ProjectBuilder @@ -595,9 +599,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(result).all { taskOutcomeEquals(shadowJarTask, FAILED) - transform { it.output }.contains( - "java.util.zip.ZipException: archive is not a ZIP archive", - ) + transform { it.output }.containsMatch("Cannot expand ZIP '.*bad\\.jar'".toRegex()) } } @@ -653,4 +655,38 @@ class JavaPluginTest : BasePluginTest() { getMainAttr("Bar-Attr").isEqualTo("Bar-Value") } } + + @Test + fun canAddExtraFilesIntoShadowJar() { + writeMainClass() + projectScriptPath.appendText( + """ + $shadowJar { + from(files('${artifactAJar.toUri().toURL().path}')) { + into('META-INF') + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries( + "my/Main.class", + "META-INF/a-1.0.jar", + ) + doesNotContainEntries(*entriesInA) + } + val unzipped = path("unzipped") + outputShadowJar.use { + it.getStream("META-INF/a-1.0.jar").use { inputStream -> + inputStream.copyTo(unzipped.outputStream()) + } + } + assertThat(jarPath(unzipped.name)).useAll { + containsEntries(*entriesInA) + doesNotContainEntries("my/Main.class") + } + } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 0d2178374..eff308c11 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -8,6 +8,7 @@ import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText +import org.gradle.api.file.DuplicatesStrategy import org.junit.jupiter.api.Test class ShadowJarCachingTest : BaseCachingTest() { @@ -210,4 +211,23 @@ class ShadowJarCachingTest : BaseCachingTest() { assertions() } + + @Test + fun shadowJarIsCachedCorrectlyAfterDuplicatesStrategyChanged() { + listOf( + DuplicatesStrategy.EXCLUDE, + DuplicatesStrategy.INCLUDE, + DuplicatesStrategy.WARN, + ).forEach { strategy -> + projectScriptPath.appendText( + """ + $shadowJar { + duplicatesStrategy = DuplicatesStrategy.$strategy + } + """.trimIndent() + System.lineSeparator(), + ) + + assertCompositeExecutions() + } + } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index d2ec31524..13232a2ca 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -1,13 +1,19 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import assertk.all import assertk.assertThat +import assertk.assertions.containsMatch import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.writeText +import org.gradle.api.file.DuplicatesStrategy +import org.gradle.testkit.runner.TaskOutcome.FAILED import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.ValueSource class ServiceFileTransformerTest : BaseTransformerTest() { @@ -39,7 +45,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { assertThat(outputShadowJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) - getContent(ENTRY_SERVICES_FOO).isEqualTo("one") + getContent(ENTRY_SERVICES_FOO).isEqualTo("two") } } @@ -189,4 +195,78 @@ class ServiceFileTransformerTest : BaseTransformerTest() { val content = outputShadowJar.use { it.getContent(servicesBarEntry) } assertThat(content).isEqualTo(CONTENT_THREE + "\n" + CONTENT_ONE_TWO) } + + /** + * See https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L54-L65. + */ + @ParameterizedTest + @MethodSource("withThrowingProvider") + fun honorDuplicatesStrategyWithThrowing( + strategy: DuplicatesStrategy, + outputRegex: String, + ) { + writeDuplicatesStrategy(strategy) + + val result = runWithFailure(shadowJarTask) + + assertThat(result).all { + taskOutcomeEquals(shadowJarTask, FAILED) + transform { it.output }.containsMatch(outputRegex.toRegex()) + } + } + + /** + * See https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L54-L65. + */ + @ParameterizedTest + @MethodSource("withoutThrowingProvider") + fun honorDuplicatesStrategyWithoutThrowing( + strategy: DuplicatesStrategy, + firstValue: String, + secondValue: String, + ) { + writeDuplicatesStrategy(strategy) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + getContent(ENTRY_SERVICES_SHADE).isEqualTo(firstValue) + getContent(ENTRY_SERVICES_FOO).isEqualTo(secondValue) + } + } + + private fun writeDuplicatesStrategy(strategy: DuplicatesStrategy) { + projectScriptPath.appendText( + """ + dependencies { + ${implementationFiles(buildJarOne(), buildJarTwo())} + } + $shadowJar { + duplicatesStrategy = DuplicatesStrategy.$strategy + mergeServiceFiles() + } + """.trimIndent(), + ) + } + + private companion object { + @JvmStatic + fun withThrowingProvider() = listOf( + Arguments.of( + DuplicatesStrategy.FAIL, + "Cannot copy zip entry .* to .* because zip entry .* has already been copied there", + ), + Arguments.of( + DuplicatesStrategy.INHERIT, + "Entry .* is a duplicate but no duplicate handling strategy has been set", + ), + ) + + @JvmStatic + fun withoutThrowingProvider() = listOf( + Arguments.of(DuplicatesStrategy.EXCLUDE, CONTENT_ONE, "one"), + Arguments.of(DuplicatesStrategy.INCLUDE, CONTENT_ONE_TWO, "one\ntwo"), + Arguments.of(DuplicatesStrategy.WARN, CONTENT_ONE_TWO, "one\ntwo"), + ) + } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt deleted file mode 100644 index 8414e617c..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultZipCompressor.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import java.io.File -import org.apache.tools.zip.Zip64Mode -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.UncheckedIOException - -/** - * Modified from [org.gradle.api.internal.file.copy.DefaultZipCompressor.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DefaultZipCompressor.java). - */ -internal class DefaultZipCompressor( - allowZip64Mode: Boolean, - private val entryCompressionMethod: Int, -) : ZipCompressor { - private val zip64Mode = if (allowZip64Mode) Zip64Mode.AsNeeded else Zip64Mode.Never - - override fun createArchiveOutputStream(destination: File): ZipOutputStream { - return try { - ZipOutputStream(destination).apply { - setUseZip64(zip64Mode) - setMethod(entryCompressionMethod) - } - } catch (e: Exception) { - throw UncheckedIOException("Unable to create ZIP output stream for file $destination.", e) - } - } -} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 3c2fb20ea..a35352ced 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -1,9 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.internal -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import java.util.regex.Pattern import org.objectweb.asm.commons.Remapper @@ -18,8 +15,6 @@ internal class RelocatorRemapper( ) : Remapper() { private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") - fun hasRelocators(): Boolean = relocators.isNotEmpty() - override fun mapValue(value: Any): Any { return if (value is String) { map(value) @@ -42,11 +37,9 @@ internal class RelocatorRemapper( for (relocator in relocators) { if (relocator.canRelocateClass(newName)) { - val classContext = RelocateClassContext(newName) - return prefix + relocator.relocateClass(classContext) + suffix + return prefix + relocator.relocateClass(newName) + suffix } else if (relocator.canRelocatePath(newName)) { - val pathContext = RelocatePathContext(newName) - return prefix + relocator.relocatePath(pathContext) + suffix + return prefix + relocator.relocatePath(newName) + suffix } } @@ -56,8 +49,4 @@ internal class RelocatorRemapper( fun mapPath(path: String): String { return map(path.substring(0, path.indexOf('.'))) } - - fun mapPath(path: ShadowCopyAction.RelativeArchivePath): String { - return mapPath(path.pathString) - } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 8c90053ad..05732babd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -1,9 +1,11 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext +import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream -import java.io.File import java.io.InputStream import java.nio.charset.Charset import java.nio.file.NoSuchFileException @@ -12,11 +14,6 @@ import java.util.Properties import java.util.jar.Attributes.Name as JarAttributeName import kotlin.io.path.toPath import org.apache.tools.zip.ZipEntry -import org.gradle.api.file.RelativePath -import org.gradle.api.internal.file.DefaultFileTreeElement -import org.gradle.internal.file.Chmod -import org.gradle.internal.file.FileMetadata -import org.gradle.internal.file.Stat /** * Known as `Main-Class` in the manifest file. @@ -33,6 +30,12 @@ internal val classPathAttributeKey = JarAttributeName.CLASS_PATH.toString() */ internal val multiReleaseAttributeKey = JarAttributeName.MULTI_RELEASE.toString() +/** + * Unsafe cast, copied from + * https://github.com/JetBrains/kotlin/blob/d3200b2c65b829b85244c4ec4cb19f6e479b06ba/core/util.runtime/src/org/jetbrains/kotlin/utils/addToStdlib.kt#L111 + */ +internal inline fun Any?.cast(): T = this as T + internal inline fun zipEntry( name: String, preserveLastModified: Boolean = true, @@ -49,17 +52,12 @@ internal inline fun zipEntry( block() } -/** - * This is used for creating a [DefaultFileTreeElement] with default values. - * [file], [chmod], and [stat] should be non-null, so they are set to dummy values here. - */ -internal fun createDefaultFileTreeElement( - file: File = DummyFile, - relativePath: RelativePath, - chmod: Chmod = DummyChmod, - stat: Stat = DummyStat, -): DefaultFileTreeElement { - return DefaultFileTreeElement(file, relativePath, chmod, stat) +internal fun Relocator.relocatePath(path: String): String { + return relocatePath(RelocatePathContext(path)) +} + +internal fun Relocator.relocateClass(className: String): String { + return relocateClass(RelocateClassContext(className)) } internal fun Properties.inputStream( @@ -88,11 +86,4 @@ internal fun requireResourceAsPath(name: String): Path { return resource.toURI().toPath() } -private val DummyFile = File("dummy") -private val DummyChmod = Chmod { _, _ -> error("This is a dummy implementation.") } -private val DummyStat = object : Stat { - override fun getUnixMode(f: File): Int = error("This is a dummy implementation.") - override fun stat(f: File): FileMetadata = error("This is a dummy implementation.") -} - private object Utils diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt deleted file mode 100644 index 14a56204b..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ZipCompressor.kt +++ /dev/null @@ -1,20 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import java.io.File -import java.io.IOException -import org.apache.tools.zip.ZipOutputStream - -/** - * Compresses the input. - * - * Modified from [org.gradle.api.internal.file.copy.ZipCompressor.java](https://github.com/gradle/gradle/blob/73091267320cd330bcb3457903436579bac354ce/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/ZipCompressor.java). - */ -public fun interface ZipCompressor { - /** - * Returns the output stream that is able to compress into the destination file - * - * @param destination the destination of the archive output stream - */ - @Throws(IOException::class) - public fun createArchiveOutputStream(destination: File): ZipOutputStream -} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index e1056f001..e4f223240 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -1,40 +1,29 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper -import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor -import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement +import com.github.jengelman.gradle.plugins.shadow.internal.cast import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import java.io.File -import java.io.InputStream -import java.io.OutputStream import java.util.GregorianCalendar import java.util.zip.ZipException -import kotlin.sequences.forEach import org.apache.tools.zip.UnixStat import org.apache.tools.zip.Zip64RequiredException import org.apache.tools.zip.ZipEntry -import org.apache.tools.zip.ZipFile import org.apache.tools.zip.ZipOutputStream import org.gradle.api.GradleException import org.gradle.api.file.FileCopyDetails -import org.gradle.api.file.FilePermissions -import org.gradle.api.file.FileTreeElement -import org.gradle.api.file.RelativePath import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.CopyActionProcessingStreamAction -import org.gradle.api.internal.file.DefaultFilePermissions import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.CopyActionProcessingStream import org.gradle.api.internal.file.copy.FileCopyDetailsInternal -import org.gradle.api.logging.Logger import org.gradle.api.logging.Logging import org.gradle.api.tasks.WorkResult import org.gradle.api.tasks.WorkResults import org.gradle.api.tasks.bundling.Zip -import org.gradle.api.tasks.util.PatternSet import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.commons.ClassRemapper @@ -44,39 +33,29 @@ import org.objectweb.asm.commons.ClassRemapper */ public open class ShadowCopyAction( private val zipFile: File, - private val compressor: ZipCompressor, + private val zosProvider: (File) -> ZipOutputStream, private val documentationRegistry: DocumentationRegistry, - private val encoding: String?, private val transformers: Set, private val relocators: Set, - private val patternSet: PatternSet, - private val preserveFileTimestamps: Boolean, private val unusedClasses: Set, + private val preserveFileTimestamps: Boolean, + private val encoding: String?, ) : CopyAction { + private val visitedDirs = mutableMapOf() override fun execute(stream: CopyActionProcessingStream): WorkResult { val zipOutStream = try { - compressor.createArchiveOutputStream(zipFile) + zosProvider(zipFile) } catch (e: Exception) { throw GradleException("Could not create ZIP '$zipFile'.", e) } try { zipOutStream.use { zos -> - stream.process( - StreamAction( - zos, - encoding, - transformers, - relocators, - patternSet, - unusedClasses, - zipFile, - preserveFileTimestamps, - logger, - ), - ) + stream.process(StreamAction(zos)) processTransformers(zos) + // This must be called as the last step to ensure that directories are added after all files. + addDirs(zos) } } catch (e: Exception) { if (e.cause is Zip64RequiredException) { @@ -100,221 +79,102 @@ public open class ShadowCopyAction( } } - public open class RelativeArchivePath( - public open val entry: ZipEntry, - private val preserveFileTimestamps: Boolean, - ) : RelativePath( - !entry.isDirectory, - // `dir/` will be split into ["dir", ""], we have to trim empty segments here. - *entry.name.split('/').filter(CharSequence::isNotEmpty).toTypedArray(), - ) { - public open val isClass: Boolean get() = lastName.endsWith(CLASS_SUFFIX) - - @Suppress("WRONG_NULLABILITY_FOR_JAVA_OVERRIDE") // It could return null in super.getParent(). - override fun getParent(): RelativeArchivePath? { - return if (segments.size <= 1) { - null - } else { - // Parent is always a directory so add / to the end of the path. - val parentPath = segments.dropLast(1).joinToString("/") + "/" - RelativeArchivePath( - zipEntry(parentPath, preserveFileTimestamps), - preserveFileTimestamps, - ) + /** + * Handles cases like [issue 53](https://github.com/GradleUp/shadow/issues/53). + */ + private fun addDirs(zos: ZipOutputStream) { + @Suppress("UNCHECKED_CAST") + val entries = zos::class.java.getDeclaredField("entries").apply { isAccessible = true } + .get(zos).cast>().map { it.name } + val added = entries.toMutableSet() + + fun addParent(name: String) { + val parent = name.substringBeforeLast('/', "") + val entryName = "$parent/" + if (parent.isNotEmpty() && added.add(entryName)) { + val details = visitedDirs[entryName] + val (lastModified, flag) = if (details == null) { + CONSTANT_TIME_FOR_ZIP_ENTRIES to UnixStat.DEFAULT_DIR_PERM + } else { + details.lastModified to details.permissions.toUnixNumeric() + } + val entry = zipEntry(entryName, preserveFileTimestamps, lastModified) { + unixMode = UnixStat.DIR_FLAG or flag + } + zos.putNextEntry(entry) + zos.closeEntry() + addParent(parent) } } - } - - public open class ArchiveFileTreeElement( - private val archivePath: RelativeArchivePath, - ) : FileTreeElement { - public open val isClass: Boolean get() = archivePath.isClass - - override fun isDirectory(): Boolean = archivePath.entry.isDirectory - - override fun getLastModified(): Long = archivePath.entry.lastModifiedDate.time - - override fun getSize(): Long = archivePath.entry.size - - override fun getName(): String = archivePath.pathString - override fun getPath(): String = archivePath.lastName - - override fun getRelativePath(): RelativeArchivePath = archivePath - - @Deprecated("Deprecated in Java") - override fun getMode(): Int = archivePath.entry.unixMode - - override fun getPermissions(): FilePermissions = DefaultFilePermissions(mode) - - public open fun asFileTreeElement(): FileTreeElement { - return createDefaultFileTreeElement(relativePath = RelativePath(!isDirectory, *archivePath.segments)) + entries.forEach { + addParent(it) } - - override fun getFile(): File = throw UnsupportedOperationException() - override fun open(): InputStream = throw UnsupportedOperationException() - override fun copyTo(outputStream: OutputStream): Unit = throw UnsupportedOperationException() - override fun copyTo(file: File): Boolean = throw UnsupportedOperationException() } - private class StreamAction( + private inner class StreamAction( private val zipOutStr: ZipOutputStream, - encoding: String?, - private val transformers: Set, - private val relocators: Set, - private val patternSet: PatternSet, - private val unusedClasses: Set, - private val zipFile: File, - private val preserveFileTimestamps: Boolean, - private val logger: Logger, ) : CopyActionProcessingStreamAction { private val remapper = RelocatorRemapper(relocators) - private val visitedFiles = mutableSetOf() init { logger.info("Relocator count: ${relocators.size}.") if (encoding != null) { - this.zipOutStr.setEncoding(encoding) + zipOutStr.setEncoding(encoding) } } override fun processFile(details: FileCopyDetailsInternal) { - if (details.isDirectory) visitDir(details) else visitFile(details) - } - - private fun visitFile(fileDetails: FileCopyDetails) { - if (fileDetails.isJar) { - processArchive(fileDetails) - } else { - try { - val isClass = fileDetails.isClass - if (!remapper.hasRelocators() || !isClass) { - if (isTransformable(fileDetails)) { - transform(fileDetails) - } else { - val mappedPath = remapper.map(fileDetails.relativePath.pathString) - val entry = zipEntry(mappedPath, preserveFileTimestamps, fileDetails.lastModified) { - unixMode = UnixStat.FILE_FLAG or fileDetails.permissions.toUnixNumeric() - } - zipOutStr.putNextEntry(entry) - fileDetails.copyTo(zipOutStr) - zipOutStr.closeEntry() - } - } else if (isClass && !isUnused(fileDetails.path)) { - remapClass(fileDetails) - } - recordVisit(fileDetails.relativePath) - } catch (e: Exception) { - throw GradleException("Could not add $fileDetails to ZIP '$zipFile'.", e) - } - } - } - - private fun visitDir(dirDetails: FileCopyDetails) { try { - // Trailing slash in name indicates that entry is a directory. - val path = dirDetails.relativePath.pathString + "/" - val entry = zipEntry(path, preserveFileTimestamps, dirDetails.lastModified) { - unixMode = UnixStat.DIR_FLAG or dirDetails.permissions.toUnixNumeric() + if (details.isDirectory) { + visitedDirs[details.path] = details + } else { + visitFile(details) } - zipOutStr.putNextEntry(entry) - zipOutStr.closeEntry() - recordVisit(dirDetails.relativePath) } catch (e: Exception) { - throw GradleException("Could not add $dirDetails to ZIP '$zipFile'.", e) + throw GradleException("Could not add $details to ZIP '$zipFile'.", e) } } - private fun recordVisit(path: RelativePath): Boolean { - return visitedFiles.add(path.pathString) - } - - private fun processArchive(fileDetails: FileCopyDetails) { - ZipFile(fileDetails.file).use { archive -> - archive.entries.asSequence() - .map { - ArchiveFileTreeElement(RelativeArchivePath(it, preserveFileTimestamps)) - } - .filter { - patternSet.asSpec.isSatisfiedBy(it.asFileTreeElement()) - }.forEach { archiveElement -> - if (archiveElement.relativePath.isFile) { - visitArchiveFile(archiveElement, archive) - } - } - } - } - - private fun visitArchiveDirectory(archiveDir: RelativeArchivePath) { - if (recordVisit(archiveDir)) { - zipOutStr.putNextEntry(archiveDir.entry) - zipOutStr.closeEntry() - } - } - - private fun visitArchiveFile(archiveFile: ArchiveFileTreeElement, archive: ZipFile) { - val archiveFilePath = archiveFile.relativePath - if (archiveFile.isClass || !isTransformable(archiveFile)) { - if (recordVisit(archiveFilePath) && !isUnused(archiveFilePath.entry.name)) { - if (!remapper.hasRelocators() || !archiveFile.isClass) { - copyArchiveEntry(archiveFilePath, archive) - } else { - remapClass(archiveFilePath, archive) - } + private fun visitFile(fileDetails: FileCopyDetails) { + val path = fileDetails.path + if (path.endsWith(".class")) { + if (isUnused(path)) return + if (relocators.isEmpty()) { + fileDetails.writeToZip(path) + return } + fileDetails.remapClass() } else { - transform(archiveFile, archive) - } - } - - private fun addParentDirectories(file: RelativeArchivePath?) { - file?.let { - addParentDirectories(it.parent) - if (!it.isFile) { - visitArchiveDirectory(it) - } + val mapped = remapper.map(path) + if (transform(fileDetails, mapped)) return + fileDetails.writeToZip(mapped) } } private fun isUnused(classPath: String): Boolean { - val classPathWithoutExtension = classPath.substringBeforeLast(".") - val className = classPathWithoutExtension.replace('/', '.') - val result = unusedClasses.contains(className) - if (result) { - logger.debug("Dropping unused class: $className") - } - return result - } - - private fun remapClass(file: RelativeArchivePath, archive: ZipFile) { - if (file.isClass) { - val entry = zipEntry(remapper.mapPath(file) + CLASS_SUFFIX, preserveFileTimestamps) - addParentDirectories(RelativeArchivePath(entry, preserveFileTimestamps)) - remapClass(archive.getInputStream(file.entry), file.pathString, file.entry.time) - } - } - - private fun remapClass(fileCopyDetails: FileCopyDetails) { - if (fileCopyDetails.isClass) { - fileCopyDetails.file.inputStream().use { - remapClass(it, fileCopyDetails.path, fileCopyDetails.lastModified) + val className = classPath.substringBeforeLast(".").replace('/', '.') + return unusedClasses.contains(className).also { + if (it) { + logger.debug("Dropping unused class: $className") } } } /** * Applies remapping to the given class with the specified relocation path. The remapped class is then written - * to the zip file. [classInputStream] is closed automatically to prevent future file leaks. - * See #364 and #408. + * to the zip file. + * + * See [issue 364](https://github.com/GradleUp/shadow/issues/364) and [issue 408](https://github.com/GradleUp/shadow/issues/408). */ - private fun remapClass(classInputStream: InputStream, path: String, lastModified: Long) { + private fun FileCopyDetails.remapClass() = file.inputStream().use { inputStream -> // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. // Copying the original constant pool should be avoided because it would keep references // to the original class names. This is not a problem at runtime (because these entries in the // constant pool are never used), but confuses some tools such as Felix's maven-bundle-plugin // that use the constant pool to determine the dependencies of a class. val cw = ClassWriter(0) - val cr = ClassReader(classInputStream) + val cr = ClassReader(inputStream) val cv = ClassRemapper(cw, remapper) try { @@ -323,70 +183,48 @@ public open class ShadowCopyAction( throw GradleException("Error in ASM processing class $path", t) } - val renamedClass = cw.toByteArray() // Temporarily remove the multi-release prefix. val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() val newPath = path.replace(multiReleasePrefix, "") val mappedName = multiReleasePrefix + remapper.mapPath(newPath) try { - // Now we put it back on so the class file is written out with the right extension. - zipOutStr.putNextEntry(zipEntry("$mappedName.class", preserveFileTimestamps, lastModified)) - renamedClass.inputStream().use { - it.copyTo(zipOutStr) + val entry = zipEntry("$mappedName.class", preserveFileTimestamps, lastModified) { + unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() } + // Now we put it back on so the class file is written out with the right extension. + zipOutStr.putNextEntry(entry) + zipOutStr.write(cw.toByteArray()) zipOutStr.closeEntry() } catch (_: ZipException) { logger.warn("We have a duplicate $mappedName in source project") } } - private fun copyArchiveEntry(archiveFile: RelativeArchivePath, archive: ZipFile) { - val mappedPath = remapper.map(archiveFile.entry.name) - val entry = zipEntry(mappedPath, preserveFileTimestamps, archiveFile.entry.time) - val mappedFile = RelativeArchivePath(entry, preserveFileTimestamps) - addParentDirectories(mappedFile) - zipOutStr.putNextEntry(mappedFile.entry) - archive.getInputStream(archiveFile.entry).use { - it.copyTo(zipOutStr) - } - zipOutStr.closeEntry() - } - - private fun transform(element: ArchiveFileTreeElement, archive: ZipFile) { - transformAndClose(element, archive.getInputStream(element.relativePath.entry)) - } - - private fun transform(details: FileCopyDetails) { - transformAndClose(details, details.file.inputStream()) - } - - private fun transformAndClose(element: FileTreeElement, inputStream: InputStream) { - inputStream.use { steam -> - val mappedPath = remapper.map(element.relativePath.pathString) - transformers.find { - it.canTransformResource(element) - }?.transform( + private fun transform(fileDetails: FileCopyDetails, mapped: String): Boolean { + val transformer = transformers.find { it.canTransformResource(fileDetails) } ?: return false + fileDetails.file.inputStream().use { inputStream -> + transformer.transform( TransformerContext( - path = mappedPath, - inputStream = steam, + path = mapped, + inputStream = inputStream, relocators = relocators, ), ) } + return true } - private fun isTransformable(element: FileTreeElement): Boolean { - return transformers.any { it.canTransformResource(element) } - } - - companion object { - private val FileCopyDetails.isClass: Boolean get() = relativePath.pathString.endsWith(CLASS_SUFFIX) - private val FileCopyDetails.isJar: Boolean get() = relativePath.pathString.endsWith(".jar") + private fun FileCopyDetails.writeToZip(entryName: String) { + val entry = zipEntry(entryName, preserveFileTimestamps, lastModified) { + unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() + } + zipOutStr.putNextEntry(entry) + copyTo(zipOutStr) + zipOutStr.closeEntry() } } public companion object { - private const val CLASS_SUFFIX = ".class" private val logger = Logging.getLogger(ShadowCopyAction::class.java) public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 8f7e59a2a..2bcfbaa19 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -2,10 +2,8 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter -import com.github.jengelman.gradle.plugins.shadow.internal.DefaultZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker -import com.github.jengelman.gradle.plugins.shadow.internal.ZipCompressor import com.github.jengelman.gradle.plugins.shadow.internal.fileCollection import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.property @@ -25,15 +23,16 @@ import java.io.File import java.io.IOException import java.util.jar.JarFile import kotlin.reflect.full.hasAnnotation +import org.apache.tools.zip.Zip64Mode import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action +import org.gradle.api.UncheckedIOException import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.copy.CopyAction -import org.gradle.api.internal.file.copy.DefaultCopySpec import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.CacheableTask @@ -48,7 +47,6 @@ import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.bundling.ZipEntryCompression -import org.gradle.api.tasks.util.PatternSet @CacheableTask public abstract class ShadowJar : @@ -56,6 +54,10 @@ public abstract class ShadowJar : ShadowSpec { private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) + private val includedZipTrees = project.provider { + includedDependencies.files.map { project.zipTree(it) } + } + init { // Shadow filters out files later. This was the default behavior in Gradle < 6.x duplicatesStrategy = DuplicatesStrategy.INCLUDE @@ -101,20 +103,6 @@ public abstract class ShadowJar : } } - @get:Internal - protected open val rootPatternSet: PatternSet - get() = (mainSpec.buildRootResolver() as DefaultCopySpec.DefaultCopySpecResolver).patternSet - - @get:Internal - protected open val internalCompressor: ZipCompressor - get() { - return when (entryCompression) { - ZipEntryCompression.DEFLATED -> DefaultZipCompressor(isZip64, ZipOutputStream.DEFLATED) - ZipEntryCompression.STORED -> DefaultZipCompressor(isZip64, ZipOutputStream.STORED) - else -> throw IllegalArgumentException("Unknown Compression type $entryCompression") - } - } - @get:Nested public open val transformers: SetProperty = objectFactory.setProperty() @@ -260,12 +248,27 @@ public abstract class ShadowJar : @TaskAction override fun copy() { - from(includedDependencies) + from(includedZipTrees.get()) injectMultiReleaseAttrIfPresent() super.copy() } override fun createCopyAction(): CopyAction { + val zosProvider = { destination: File -> + try { + val entryCompressionMethod = when (entryCompression) { + ZipEntryCompression.DEFLATED -> ZipOutputStream.DEFLATED + ZipEntryCompression.STORED -> ZipOutputStream.STORED + else -> throw IllegalArgumentException("Unknown Compression type $entryCompression.") + } + ZipOutputStream(destination).apply { + setUseZip64(if (isZip64) Zip64Mode.AsNeeded else Zip64Mode.Never) + setMethod(entryCompressionMethod) + } + } catch (e: Exception) { + throw UncheckedIOException("Unable to create ZIP output stream for file $destination.", e) + } + } val documentationRegistry = services.get(DocumentationRegistry::class.java) val unusedClasses = if (minimizeJar.get()) { val unusedTracker = UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) @@ -278,14 +281,13 @@ public abstract class ShadowJar : } return ShadowCopyAction( archiveFile.get().asFile, - internalCompressor, + zosProvider, documentationRegistry, - metadataCharset, transformers.get(), relocators.get() + packageRelocators, - rootPatternSet, - isPreserveFileTimestamps, unusedClasses, + isPreserveFileTimestamps, + metadataCharset, ) } @@ -315,7 +317,6 @@ public abstract class ShadowJar : } private fun injectMultiReleaseAttrIfPresent() { - // TODO: https://github.com/GradleUp/shadow/pull/1239#discussion_r1946064032. val includeMultiReleaseAttr = includedDependencies.files.any { try { JarFile(it).use { jarFile -> diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index 71a5e718d..4673b9074 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -12,7 +12,7 @@ import org.gradle.api.file.FileTreeElement @CacheableTransformer public open class ApacheLicenseResourceTransformer : Transformer by NoOpTransformer { override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString + val path = element.path return LICENSE_PATH.equals(path, ignoreCase = true) || LICENSE_TXT_PATH.regionMatches(0, path, 0, LICENSE_TXT_PATH.length, ignoreCase = true) || LICENSE_MD_PATH.regionMatches(0, path, 0, LICENSE_MD_PATH.length, ignoreCase = true) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index e9fafdc53..114651658 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -74,7 +74,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( public open val charsetName: Property = objectFactory.property(Charsets.UTF_8.name()) override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString + val path = element.path return NOTICE_PATH.equals(path, ignoreCase = true) || NOTICE_TXT_PATH.equals(path, ignoreCase = true) || NOTICE_MD_PATH.equals(path, ignoreCase = true) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index c0fdcfa8a..929d37bb9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -37,17 +37,15 @@ public open class AppendingTransformer @Inject constructor( public open val separator: Property = objectFactory.property(DEFAULT_SEPARATOR) override fun canTransformResource(element: FileTreeElement): Boolean { - return resource.orNull.equals(element.relativePath.pathString, ignoreCase = true) + return resource.orNull.equals(element.path, ignoreCase = true) } override fun transform(context: TransformerContext) { - context.inputStream.use { - if (data.size() > 0) { - // Append the separator before the new content to ensure the separator is not at the end of the file. - data.write(separator.get().toByteArray()) - } - it.copyTo(data) + if (data.size() > 0) { + // Append the separator before the new content to ensure the separator is not at the end of the file. + data.write(separator.get().toByteArray()) } + context.inputStream.copyTo(data) } override fun hasTransformedResource(): Boolean { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index 458765800..80041930d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -26,7 +26,7 @@ public open class ComponentsXmlResourceTransformer : Transformer { private val components = mutableMapOf() override fun canTransformResource(element: FileTreeElement): Boolean { - return COMPONENTS_XML_PATH == element.relativePath.pathString + return COMPONENTS_XML_PATH == element.path } override fun transform(context: TransformerContext) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index c88badee5..fdb526108 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -24,7 +24,6 @@ public open class DontIncludeResourceTransformer @Inject constructor( public open val resource: Property = objectFactory.property() override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString - return !resource.orNull.isNullOrEmpty() && path.endsWith(resource.get()) + return !resource.orNull.isNullOrEmpty() && element.path.endsWith(resource.get()) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index 2a1dec80e..aacf8aadd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -34,7 +34,7 @@ public open class GroovyExtensionModuleTransformer : Transformer { private var legacy = true override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString + val path = element.path if (path == PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) { // Groovy 2.5+ legacy = false diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index 94ce05001..668c284aa 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -35,17 +35,13 @@ public open class Log4j2PluginsCacheFileTransformer : Transformer { private val tempRelocators = mutableListOf() override fun canTransformResource(element: FileTreeElement): Boolean { - return PLUGIN_CACHE_FILE == element.relativePath.pathString + return PLUGIN_CACHE_FILE == element.path } override fun transform(context: TransformerContext) { val temporaryFile = createTempFile("Log4j2Plugins", ".dat") tempFiles.add(temporaryFile) - val fos = temporaryFile.outputStream() - context.inputStream.use { - it.copyTo(fos) - } - + context.inputStream.copyTo(temporaryFile.outputStream()) tempRelocators.addAll(context.relocators) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 64e287a0c..32c9098f0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -31,17 +31,15 @@ public open class ManifestAppenderTransformer @Inject constructor( public open val attributes: SetProperty>> = objectFactory.setProperty() override fun canTransformResource(element: FileTreeElement): Boolean { - return MANIFEST_NAME.equals(element.relativePath.pathString, ignoreCase = true) + return MANIFEST_NAME.equals(element.path, ignoreCase = true) } override fun transform(context: TransformerContext) { if (manifestContents.isEmpty()) { try { - context.inputStream.use { inputStream -> - val outputStream = ByteArrayOutputStream() - inputStream.copyTo(outputStream) - manifestContents = outputStream.toByteArray() - } + val outputStream = ByteArrayOutputStream() + context.inputStream.copyTo(outputStream) + manifestContents = outputStream.toByteArray() } catch (e: IOException) { logger.warn("Failed to read MANIFEST.MF", e) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 9ebbe3ea8..7e2ca1726 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -43,8 +43,7 @@ public open class ManifestResourceTransformer @Inject constructor( public open val manifestEntries: MapProperty = objectFactory.mapProperty() override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.relativePath.pathString - return JarFile.MANIFEST_NAME.equals(path, ignoreCase = true) + return JarFile.MANIFEST_NAME.equals(element.path, ignoreCase = true) } override fun transform(context: TransformerContext) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 1d5a076b1..0803bd1ae 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -129,7 +129,7 @@ public open class PropertiesFileTransformer @Inject constructor( val mappings = mappings.get() val paths = paths.get() - val path = element.relativePath.pathString + val path = element.path if (path in mappings) return true for (key in mappings.keys) { if (key.toRegex().containsMatchIn(path)) return true diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index d4e00ffe7..0b5628743 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement @@ -42,8 +41,7 @@ public open class ServiceFileTransformer( } override fun canTransformResource(element: FileTreeElement): Boolean { - val target = if (element is ShadowCopyAction.ArchiveFileTreeElement) element.asFileTreeElement() else element - return patternSet.asSpec.isSatisfiedBy(target) + return patternSet.asSpec.isSatisfiedBy(element) } override fun transform(context: TransformerContext) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index a01790a39..ca7f6eabb 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -41,7 +41,7 @@ public open class XmlAppendingTransformer @Inject constructor( public open val resource: Property = objectFactory.property() override fun canTransformResource(element: FileTreeElement): Boolean { - return resource.orNull?.equals(element.relativePath.pathString, ignoreCase = true) == true + return resource.orNull?.equals(element.path, ignoreCase = true) == true } override fun transform(context: TransformerContext) { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 645740243..4a10937a0 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -4,6 +4,8 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.internal.relocateClass +import com.github.jengelman.gradle.plugins.shadow.internal.relocatePath import org.junit.jupiter.api.Test /** @@ -383,13 +385,5 @@ class SimpleRelocatorTest { } } """.trimIndent() - - fun SimpleRelocator.relocatePath(path: String): String { - return relocatePath(RelocatePathContext(path)) - } - - fun SimpleRelocator.relocateClass(className: String): String { - return relocateClass(RelocateClassContext(className)) - } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 30bd720dd..5645168e7 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -1,8 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import com.github.jengelman.gradle.plugins.shadow.internal.createDefaultFileTreeElement import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create +import com.github.jengelman.gradle.plugins.shadow.util.noOpDelegate import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import java.lang.reflect.ParameterizedType import java.nio.file.Path @@ -11,6 +11,7 @@ import java.util.zip.ZipFile import kotlin.io.path.createTempFile import kotlin.io.path.outputStream import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath import org.junit.jupiter.api.BeforeEach @@ -31,8 +32,12 @@ abstract class BaseTransformerTest { protected companion object { const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" - fun Transformer.canTransformResource(path: String): Boolean { - val element = createDefaultFileTreeElement(relativePath = RelativePath.parse(true, path)) + fun Transformer.canTransformResource(path: String, isFile: Boolean = true): Boolean { + val element = object : FileTreeElement by noOpDelegate() { + private val _relativePath = RelativePath.parse(isFile, path) + override fun getPath(): String = _relativePath.pathString + override fun getRelativePath(): RelativePath = _relativePath + } return canTransformResource(element) } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt index 638e5b09e..0f7168379 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt @@ -1,12 +1,21 @@ package com.github.jengelman.gradle.plugins.shadow.util import java.io.OutputStream +import java.lang.reflect.InvocationHandler +import java.lang.reflect.Proxy import org.apache.tools.zip.ZipOutputStream import org.gradle.api.model.ObjectFactory import org.gradle.testfixtures.ProjectBuilder val testObjectFactory: ObjectFactory = ProjectBuilder.builder().build().objects +val NO_OP_HANDLER = InvocationHandler { _, _, _ -> } + +inline fun noOpDelegate(): T { + val javaClass = T::class.java + return Proxy.newProxyInstance(javaClass.classLoader, arrayOf(javaClass), NO_OP_HANDLER) as T +} + fun OutputStream.zipOutputStream(): ZipOutputStream { return this as? ZipOutputStream ?: ZipOutputStream(this) } From 30a4fa5e91691f1cf9edf112655cff09813c966a Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 24 Feb 2025 18:49:06 +0800 Subject: [PATCH 269/941] Prepare version 9.0.0-beta9 --- gradle.properties | 2 +- src/docs/changes/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/gradle.properties b/gradle.properties index d0b5cdb89..c73a890c2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta9-SNAPSHOT +VERSION_NAME=9.0.0-beta9 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 81d7b4762..cde8a5122 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased] + +## [v9.0.0-beta9] (2025-02-24) + **Added** - Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) @@ -562,7 +565,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Mon, 24 Feb 2025 18:50:22 +0800 Subject: [PATCH 270/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index c73a890c2..69c60af1a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta9 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 208bad1e5f68361cf2fd1d829962c30de642454f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Feb 2025 04:05:35 -0500 Subject: [PATCH 271/941] Fix the last modified time of shadowed directories (#1277) * Add `preserveLastModifiedCorrectly` * Use parent as the key in addDirs.addParent * Fxi things * Use ParameterizedTest * Update baseline * Inject `enableRelocation` as test param as well * Update relocated dir time * Minus 3 sec instead of 1 min in test * Update changelog * Check them in time range --- lint-baseline.xml | 10 +- src/docs/changes/README.md | 4 + .../gradle/plugins/shadow/BasePluginTest.kt | 8 +- .../gradle/plugins/shadow/RelocationTest.kt | 93 +++++++++++++++++++ .../plugins/shadow/tasks/ShadowCopyAction.kt | 11 ++- 5 files changed, 115 insertions(+), 11 deletions(-) diff --git a/lint-baseline.xml b/lint-baseline.xml index ec96ac130..eca3ea723 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -41,7 +41,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -52,7 +52,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -63,7 +63,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -74,7 +74,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -85,7 +85,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index cde8a5122..68ee373df 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Fixed** + +- Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) + ## [v9.0.0-beta9] (2025-02-24) 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 23b5c6670..a2cadc5a4 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 @@ -16,6 +16,7 @@ import com.github.jengelman.gradle.plugins.shadow.util.JarPath import java.io.Closeable import java.nio.file.Path import java.util.Properties +import java.util.jar.JarEntry import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.Path import kotlin.io.path.absolutePathString @@ -348,11 +349,10 @@ abstract class BasePluginTest { } val junitJar: Path = requireResourceAsPath("junit-3.8.2.jar") - val junitEntries: Array = JarPath(junitJar) + val junitRawEntries: List = JarPath(junitJar) .use { it.entries().toList() } - .map { entry -> entry.name } - .filterNot { it == "junit3.8.2/" || it.startsWith("META-INF/") } - .toTypedArray() + .filterNot { it.name == "junit3.8.2/" || it.name.startsWith("META-INF/") } + val junitEntries: Array = junitRawEntries.map { it.name }.toTypedArray() val shadowJar: String = """ tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name}) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index af1f6d2d2..b56da6009 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -8,12 +8,14 @@ import assertk.assertions.isInstanceOf import assertk.assertions.isNotEmpty import assertk.fail import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.Companion.CONSTANT_TIME_FOR_ZIP_ENTRIES import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import java.net.URLClassLoader import kotlin.io.path.appendText import kotlin.io.path.writeText +import kotlin.time.Duration.Companion.seconds import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -347,6 +349,89 @@ class RelocationTest : BasePluginTest() { assertThat(excluded).isEmpty() } + @ParameterizedTest + @MethodSource("preserveLastModifiedProvider") + fun preserveLastModifiedCorrectly(enableRelocation: Boolean, preserveFileTimestamps: Boolean) { + // Minus 3 sec to avoid the time difference between the file system and the JVM. + val currentTimeMillis = System.currentTimeMillis() - 3.seconds.inWholeMilliseconds + val junitEntryTimeRange = junitRawEntries.map { it.time }.let { it.min()..it.max() } + writeMainClass(withImports = true) + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + enableRelocation = $enableRelocation + preserveFileTimestamps = $preserveFileTimestamps + } + """.trimIndent(), + ) + + run(shadowJarTask) + + if (enableRelocation) { + val (relocatedEntries, otherEntries) = outputShadowJar.use { + it.entries().toList().partition { entry -> entry.name.startsWith("shadow/") } + } + assertThat(relocatedEntries).isNotEmpty() + assertThat(otherEntries).isNotEmpty() + val (relocatedDirs, relocatedClasses) = relocatedEntries.partition { it.isDirectory } + assertThat(relocatedDirs).isNotEmpty() + assertThat(relocatedClasses).isNotEmpty() + + if (preserveFileTimestamps) { + relocatedClasses.forEach { entry -> + // Relocated files should preserve the last modified time of the original files. + if (entry.time !in junitEntryTimeRange) { + fail("Relocated file ${entry.name} has an invalid last modified time: ${entry.time}") + } + } + (relocatedDirs + otherEntries).forEach { entry -> + // Relocated directories and other entries are newly created, so they should be in now time. + if (entry.time < currentTimeMillis) { + fail("Relocated directory ${entry.name} has an invalid last modified time: ${entry.time}") + } + } + } else { + (relocatedEntries + otherEntries).forEach { entry -> + // All entries should be newly modified, that default to CONSTANT_TIME_FOR_ZIP_ENTRIES. + if (entry.time != CONSTANT_TIME_FOR_ZIP_ENTRIES) { + fail("Entry ${entry.name} has an invalid last modified time: ${entry.time}") + } + } + } + } else { + val (shadowedEntries, otherEntries) = outputShadowJar.use { + it.entries().toList().partition { entry -> entry.name.startsWith("junit/") } + } + assertThat(shadowedEntries).isNotEmpty() + assertThat(otherEntries).isNotEmpty() + + if (preserveFileTimestamps) { + shadowedEntries.forEach { entry -> + // Shadowed entries should preserve the last modified time of the original entries. + if (entry.time !in junitEntryTimeRange) { + fail("Shadowed entry ${entry.name} has an invalid last modified time: ${entry.time}") + } + } + otherEntries.forEach { entry -> + // Other entries are newly created, so they should be in now time. + if (entry.time < currentTimeMillis) { + fail("Entry ${entry.name} has an invalid last modified time: ${entry.time}") + } + } + } else { + (shadowedEntries + otherEntries).forEach { entry -> + // All entries should be newly modified, defaults to CONSTANT_TIME_FOR_ZIP_ENTRIES. + if (entry.time != CONSTANT_TIME_FOR_ZIP_ENTRIES) { + fail("Entry ${entry.name} has an invalid last modified time: ${entry.time}") + } + } + } + } + } + private companion object { @JvmStatic fun prefixProvider() = listOf( @@ -355,5 +440,13 @@ class RelocationTest : BasePluginTest() { Arguments.of("new.pkg"), Arguments.of("new/path"), ) + + @JvmStatic + fun preserveLastModifiedProvider() = listOf( + Arguments.of(false, false), + Arguments.of(true, false), + Arguments.of(false, true), + Arguments.of(true, true), + ) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index e4f223240..75c649e93 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -87,14 +87,15 @@ public open class ShadowCopyAction( val entries = zos::class.java.getDeclaredField("entries").apply { isAccessible = true } .get(zos).cast>().map { it.name } val added = entries.toMutableSet() + val currentTimeMillis = System.currentTimeMillis() fun addParent(name: String) { val parent = name.substringBeforeLast('/', "") val entryName = "$parent/" if (parent.isNotEmpty() && added.add(entryName)) { - val details = visitedDirs[entryName] + val details = visitedDirs[parent] val (lastModified, flag) = if (details == null) { - CONSTANT_TIME_FOR_ZIP_ENTRIES to UnixStat.DEFAULT_DIR_PERM + currentTimeMillis to UnixStat.DEFAULT_DIR_PERM } else { details.lastModified to details.permissions.toUnixNumeric() } @@ -226,6 +227,12 @@ public open class ShadowCopyAction( public companion object { private val logger = Logging.getLogger(ShadowCopyAction::class.java) + + /** + * A copy of [org.gradle.api.internal.file.archive.ZipEntryConstants.CONSTANT_TIME_FOR_ZIP_ENTRIES]. + * + * 1980-02-01 00:00:00 (318182400000). + */ public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis } } From 866055f4d304842b86d37c0add4788c93afd711f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Feb 2025 04:28:43 -0500 Subject: [PATCH 272/941] Replace TODOs for issue 1202 (#1278) --- .../github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 4 ++-- .../plugins/shadow/transformers/ServiceFileTransformer.kt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 2bcfbaa19..d0768b133 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -141,10 +141,10 @@ public abstract class ShadowJar : @Internal override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest - @Input // TODO: https://github.com/GradleUp/shadow/issues/1202. + @Input // Trigger task executions after includes changed. override fun getIncludes(): MutableSet = super.getIncludes() - @Input // TODO: https://github.com/GradleUp/shadow/issues/1202. + @Input // Trigger task executions after excludes changed. override fun getExcludes(): MutableSet = super.getExcludes() override fun minimize(): ShadowJar = apply { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 0b5628743..d5e9a54ec 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -78,10 +78,10 @@ public open class ServiceFileTransformer( } } - @Input // TODO: https://github.com/GradleUp/shadow/issues/1202. + @Input // Trigger task executions after includes changed. override fun getIncludes(): MutableSet = patternSet.includes - @Input // TODO: https://github.com/GradleUp/shadow/issues/1202. + @Input // Trigger task executions after excludes changed. override fun getExcludes(): MutableSet = patternSet.excludes private companion object { From bddc93eeab112b7f9639ab2623b0db78b651798c Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 25 Feb 2025 17:30:54 +0800 Subject: [PATCH 273/941] Update the comment about duplicatesStrategy --- .../shadow/transformers/ServiceFileTransformerTest.kt | 6 ------ .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 13232a2ca..13aabc361 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -196,9 +196,6 @@ class ServiceFileTransformerTest : BaseTransformerTest() { assertThat(content).isEqualTo(CONTENT_THREE + "\n" + CONTENT_ONE_TWO) } - /** - * See https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L54-L65. - */ @ParameterizedTest @MethodSource("withThrowingProvider") fun honorDuplicatesStrategyWithThrowing( @@ -215,9 +212,6 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } - /** - * See https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L54-L65. - */ @ParameterizedTest @MethodSource("withoutThrowingProvider") fun honorDuplicatesStrategyWithoutThrowing( diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index d0768b133..59d16588d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -59,7 +59,7 @@ public abstract class ShadowJar : } init { - // Shadow filters out files later. This was the default behavior in Gradle < 6.x + // https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L55-L64 duplicatesStrategy = DuplicatesStrategy.INCLUDE manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) From c61bcb0faea0a7c215dc3b69a4851e5812ce63b1 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 25 Feb 2025 17:38:28 +0800 Subject: [PATCH 274/941] Simplify yarn_build --- .../src/main/kotlin/shadow.convention.deploy.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts index 60cfb44fc..986d56d57 100644 --- a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts @@ -7,8 +7,8 @@ plugins { val yarnBuild = tasks.named("yarn_build") { dependsOn(tasks.yarn) - inputs.files(fileTree("src/docs")) - outputs.dir(file("build/site")) + inputs.dir("src/docs") + outputs.dir("build/site") } gitPublish { From 250c30282e31b1bb4e8d27507d5f75c5c3a6b7f3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 25 Feb 2025 18:26:49 +0800 Subject: [PATCH 275/941] Update dependency gradle to v8.13 (#1279) * Update dependency gradle to v8.13 * Remove `testType` --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- build.gradle.kts | 2 -- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 5196939d0..81ba15916 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -73,7 +73,6 @@ testing.suites { } } register("integrationTest") { - testType = TestSuiteType.INTEGRATION_TEST targets.configureEach { testTask { val docsDir = file("src/docs") @@ -84,7 +83,6 @@ testing.suites { } } register("functionalTest") { - testType = TestSuiteType.FUNCTIONAL_TEST targets.configureEach { testTask { // Required to enable `IssueExtension` for all tests. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e18bc253b..37f853b1c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From de7dd00d0387c736ef1592a30a1b436b233e8677 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 10:38:48 +0800 Subject: [PATCH 276/941] Update changelog --- 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 68ee373df..a29f644aa 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -19,7 +19,7 @@ - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) - Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) - **BREAKING CHANGE:** Move `DependencyFilter` from `com.github.jengelman.gradle.plugins.shadow.internal` into `com.github.jengelman.gradle.plugins.shadow.tasks`. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) -- Handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) **Fixed** From b28f0cd64dae0ce75b76eb95ad285e30b779bc8a Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 10:41:02 +0800 Subject: [PATCH 277/941] Reapply "Try out ubuntu-24.04-arm on CI (#1164)" (#1230) This reverts commit 6b570f5bc0e20c632160a2b73d7ad029c4a2111d. --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c393c30b..38b7e36f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - os: [ ubuntu-latest, windows-latest ] + os: [ ubuntu-24.04-arm, windows-latest ] # Always test on the latest version and some LTS. java: [ 17, 21, 23 ] runs-on: ${{ matrix.os }} @@ -26,7 +26,7 @@ jobs: publish-snapshot: needs: build - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm if: github.repository == 'GradleUp/shadow' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3e9d3ca1..1ef4b7ca1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm if: github.repository == 'GradleUp/shadow' permissions: contents: write From f1f87366c448784f12b1e68758347b3ddc6ab7c5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Feb 2025 23:18:20 -0500 Subject: [PATCH 278/941] Defer runtimeConfiguration convention (#1281) --- .../github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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..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 @@ -53,7 +53,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( } } task.from(sourceSets.named("main").map { it.output }) - task.configurations.convention(listOf(runtimeConfiguration)) + task.configurations.convention(provider { listOf(runtimeConfiguration) }) task.exclude( "META-INF/INDEX.LIST", "META-INF/*.SF", From f0fbbc768160f90787ba2d8851ae6e9ca2530c51 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 03:25:37 -0500 Subject: [PATCH 279/941] Unify jar Attributes usages (#1282) --- .../plugins/shadow/transformers/TransformersTest.kt | 4 ++-- .../transformers/ManifestResourceTransformer.kt | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 19a57cecb..62dca788f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -11,7 +11,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.getStream -import java.util.jar.Attributes +import java.util.jar.Attributes as JarAttribute import kotlin.io.path.appendText import kotlin.io.path.writeText import kotlin.reflect.KClass @@ -152,7 +152,7 @@ class TransformersTest : BaseTransformerTest() { } private fun commonAssertions( - mainAttributesBlock: Attributes.() -> Unit = { + mainAttributesBlock: JarAttribute.() -> Unit = { assertThat(getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("PASSED") assertThat(getValue(mainClassAttributeKey)).isEqualTo("my.Main") assertThat(getValue(NEW_ENTRY_ATTR_KEY)).isEqualTo("NEW") diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 7e2ca1726..9e03e330e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -1,10 +1,11 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import java.io.IOException -import java.util.jar.Attributes +import java.util.jar.Attributes as JarAttribute import java.util.jar.JarFile import java.util.jar.Manifest import javax.inject.Inject @@ -40,7 +41,7 @@ public open class ManifestResourceTransformer @Inject constructor( @get:Optional @get:Input - public open val manifestEntries: MapProperty = objectFactory.mapProperty() + public open val manifestEntries: MapProperty = objectFactory.mapProperty() override fun canTransformResource(element: FileTreeElement): Boolean { return JarFile.MANIFEST_NAME.equals(element.path, ignoreCase = true) @@ -70,17 +71,17 @@ public open class ManifestResourceTransformer @Inject constructor( val attributes = manifest!!.mainAttributes mainClass.orNull?.let { - attributes[Attributes.Name.MAIN_CLASS] = it + attributes[mainClassAttributeKey] = it } manifestEntries.get().forEach { (key, value) -> - attributes[Attributes.Name(key)] = value + attributes[JarAttribute.Name(key)] = value } os.putNextEntry(zipEntry(JarFile.MANIFEST_NAME, preserveFileTimestamps)) manifest!!.write(os) } - public open fun attributes(attributes: Map): ManifestResourceTransformer = apply { + public open fun attributes(attributes: Map): ManifestResourceTransformer = apply { manifestEntries.putAll(attributes) } From 06c6ae41605f1bb4ddebd2ac7044013e76e43aaf Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 05:03:59 -0500 Subject: [PATCH 280/941] Compat Kotlin Multiplatform plugin (#1280) * Introduce KMP dependency * Apply `ShadowJavaPlugin` for org.jetbrains.kotlin.multiplatform * Configure `from` and `configurations` for org.jetbrains.kotlin.multiplatform * Configure kmp related logic in `ShadowKmpPlugin` * Test `compatKmpJvmTarget` * Don't combine java plugin with kmp plugin * Extract `registerShadowJarCommon` * Check main class as well * Add `friends` configuration https://www.liutikas.net/2025/01/12/Kotlin-Library-Friends.html * Revert "Add `friends` configuration" This reverts commit 48a84b44e60dc6ca578d41387717b6ad85c86ddb. * Update changelog * New doc for "Integrating with Kotlin Multiplatform Plugin" * Update src/docs/kmp-plugin/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Refine * Tweak style * Improve `registerShadowJarCommon`'s interoperability for Groovy --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 8 +++ build.gradle.kts | 2 + gradle/libs.versions.toml | 6 +- src/docs/.vuepress/config.js | 1 + src/docs/changes/README.md | 5 ++ src/docs/kmp-plugin/README.md | 37 +++++++++++++ .../gradle/plugins/shadow/BasePluginTest.kt | 55 ++++++++++++------- .../gradle/plugins/shadow/KmpPluginTest.kt | 54 ++++++++++++++++++ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 36 +++++++----- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 24 ++++++++ .../gradle/plugins/shadow/ShadowPlugin.kt | 4 ++ 11 files changed, 198 insertions(+), 34 deletions(-) create mode 100644 src/docs/kmp-plugin/README.md create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt 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..5570a339b 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -51,11 +51,19 @@ 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;Lorg/gradle/api/Action;)Lorg/gradle/api/tasks/TaskProvider; +} + +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 { 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" 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/changes/README.md b/src/docs/changes/README.md index a29f644aa..6b9c854f4 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 need to manually configure `manifest.attributes` (e.g. `Main-Class` attr) in the `shadowJar` task if necessary. + **Fixed** - Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) diff --git a/src/docs/kmp-plugin/README.md b/src/docs/kmp-plugin/README.md new file mode 100644 index 000000000..207c91e29 --- /dev/null +++ b/src/docs/kmp-plugin/README.md @@ -0,0 +1,37 @@ +# 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 its `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' + } +} +``` 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..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 @@ -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 @@ -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 new file mode 100644 index 000000000..0cd58c3ce --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt @@ -0,0 +1,54 @@ +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() { + val mainClass = writeMainClass(sourceSet = "jvmMain", isJava = false) + projectScriptPath.appendText( + """ + kotlin { + jvm() + sourceSets { + commonMain { + dependencies { + implementation 'my:b:1.0' + } + } + jvmMain { + dependencies { + implementation 'my:a:1.0' + } + } + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries( + mainClass, + *entriesInAB, + ) + } + } +} 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..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 @@ -38,10 +39,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 +52,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 +121,26 @@ public abstract class ShadowJavaPlugin @Inject constructor( public inline val ConfigurationContainer.shadowRuntimeElements: NamedDomainObjectProvider get() = named(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) + + @JvmStatic + public fun Project.registerShadowJarCommon( + action: Action, + ): 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.execute(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 new file mode 100644 index 000000000..c0c443041 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt @@ -0,0 +1,24 @@ +package com.github.jengelman.gradle.plugins.shadow + +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 + +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") + 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 c7c7f908a..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 @@ -19,6 +19,10 @@ public abstract class ShadowPlugin : Plugin { withType(ApplicationPlugin::class.java) { apply(ShadowApplicationPlugin::class.java) } + withId("org.jetbrains.kotlin.multiplatform") { + apply(ShadowKmpPlugin::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 8cde6c14bdd6697c5e0d2e560e6b7d6df7cf1626 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 18:12:32 +0800 Subject: [PATCH 281/941] Revert "Reapply "Try out ubuntu-24.04-arm on CI (#1164)" (#1230)" This reverts commit b28f0cd64dae0ce75b76eb95ad285e30b779bc8a. --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38b7e36f3..4c393c30b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: build: strategy: matrix: - os: [ ubuntu-24.04-arm, windows-latest ] + os: [ ubuntu-latest, windows-latest ] # Always test on the latest version and some LTS. java: [ 17, 21, 23 ] runs-on: ${{ matrix.os }} @@ -26,7 +26,7 @@ jobs: publish-snapshot: needs: build - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-latest if: github.repository == 'GradleUp/shadow' && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1ef4b7ca1..a3e9d3ca1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-latest if: github.repository == 'GradleUp/shadow' permissions: contents: write From 4a96b3ef41af461e88e5cdfece58405540e7adfa Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 06:57:21 -0500 Subject: [PATCH 282/941] Show how to publish custom ShadowJar outputs (#1283) * Test `publishCustomShadowJar` * Doc "Publish Custom Shadowed JAR with the Custom ShadowJar Task" https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:publications https://docs.gradle.org/current/userguide/publishing_customization.html#sec:publishing_custom_artifacts_to_maven https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html#org.gradle.api.publish.maven.MavenPublication:artifact(java.lang.Object) --- src/docs/publishing/README.md | 39 +++++++++++++++++++ .../gradle/plugins/shadow/PublishingTest.kt | 32 +++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/src/docs/publishing/README.md b/src/docs/publishing/README.md index 15172841d..53b2de5fa 100644 --- a/src/docs/publishing/README.md +++ b/src/docs/publishing/README.md @@ -90,3 +90,42 @@ publishing { } } ``` + + +## Publish Custom ShadowJar Task Outputs + +It is possible to publish a custom `ShadowJar` task's output via the [`MavenPublication.artifact(java.lang.Object)`](https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html#org.gradle.api.publish.maven.MavenPublication:artifact(java.lang.Object)) method. + +```groovy +// Publishing a Shadow JAR with the Maven-Publish Plugin +plugins { + id 'java' + id 'maven-publish' + id 'com.gradleup.shadow' +} + +def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + description = "Create a combined JAR of project and test dependencies" + archiveClassifier = "tests" + from sourceSets.test.output + configurations = [project.configurations.testRuntimeClasspath] +} + +dependencies { + testImplementation 'junit:junit:3.8.2' +} + +publishing { + publications { + shadow(MavenPublication) { + artifact(testShadowJar) + } + } + repositories { + maven { + url = "https://repo.myorg.com" + } + } +} +``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index a068fc424..12d3c83a7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -9,6 +9,7 @@ import assertk.assertions.isEqualTo import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath @@ -140,6 +141,37 @@ class PublishingTest : BasePluginTest() { assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my/maven/1.0/maven-1.0.module"))) } + @Test + fun publishCustomShadowJar() { + projectScriptPath.appendText( + publishConfiguration( + projectBlock = """ + def testShadowJar = tasks.register('testShadowJar', ${ShadowJar::class.java.name}) { + group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + description = "Create a combined JAR of project and test dependencies" + archiveClassifier = "tests" + from sourceSets.test.output + configurations = [project.configurations.testRuntimeClasspath] + } + """.trimIndent(), + dependenciesBlock = """ + testImplementation 'junit:junit:3.8.2' + """.trimIndent(), + publicationsBlock = """ + shadow(MavenPublication) { + artifact testShadowJar + } + """.trimIndent(), + ), + ) + + publish() + + assertThat(repoJarPath("my/maven/1.0/maven-1.0-tests.jar")).useAll { + containsEntries(*junitEntries) + } + } + @ParameterizedTest @ValueSource(booleans = [false, true]) fun publishShadowedGradlePlugin(legacy: Boolean) { From cfb0b4c91131ccca644693d0607bae95d9538b5b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 09:27:39 -0500 Subject: [PATCH 283/941] Update the usage of ShadowJar.from (#1285) --- src/docs/configuration/README.md | 6 +++++- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/docs/configuration/README.md b/src/docs/configuration/README.md index 950514c68..41c2d0db0 100644 --- a/src/docs/configuration/README.md +++ b/src/docs/configuration/README.md @@ -109,8 +109,12 @@ method can be used to add extra files. ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { from('extra.jar') { - // Copy the contents of the extra.jar file into META-INF/ in the shadowed JAR. + // Copy extra.jar file (without unzipping) into META-INF/ in the shadowed JAR. into('META-INF') } + from('Foo') { + // Copy Foo file into Bar/ in the shadowed JAR. + into('Bar') + } } ``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 4d4521bee..832ee671b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -20,6 +20,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.getContent import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.getStream import com.github.jengelman.gradle.plugins.shadow.util.runProcess @@ -659,12 +660,16 @@ class JavaPluginTest : BasePluginTest() { @Test fun canAddExtraFilesIntoShadowJar() { writeMainClass() + path("Foo").writeText("Foo") projectScriptPath.appendText( """ $shadowJar { from(files('${artifactAJar.toUri().toURL().path}')) { into('META-INF') } + from('Foo') { + into('Bar') + } } """.trimIndent(), ) @@ -675,8 +680,10 @@ class JavaPluginTest : BasePluginTest() { containsEntries( "my/Main.class", "META-INF/a-1.0.jar", + "Bar/Foo", ) doesNotContainEntries(*entriesInA) + getContent("Bar/Foo").isEqualTo("Foo") } val unzipped = path("unzipped") outputShadowJar.use { From 7c5f4745991831c02f62527590fba2982657a0e9 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Feb 2025 22:35:12 +0800 Subject: [PATCH 284/941] Fix ktor dependencies in doc --- src/docs/kmp-plugin/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docs/kmp-plugin/README.md b/src/docs/kmp-plugin/README.md index 207c91e29..57dee7947 100644 --- a/src/docs/kmp-plugin/README.md +++ b/src/docs/kmp-plugin/README.md @@ -17,12 +17,12 @@ kotlin { sourceSets { commonMain { dependencies { - implementation "io.ktor:ktor-client-core$ktorVersion" + implementation "io.ktor:ktor-client-core:$ktorVersion" } } jvmMain { dependencies { - implementation "io.ktor:ktor-client-okhttp$ktorVersion" + implementation "io.ktor:ktor-client-okhttp:$ktorVersion" } } } From ee5197ff2f9a53f0a279043f8fba27dd8b5bf5d6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Feb 2025 09:07:17 +0800 Subject: [PATCH 285/941] Update plugin android-lint to v8.8.2 (#1286) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 07266742a..dfb606e9c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,6 +34,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.8.1" +android-lint = "com.android.lint:8.8.2" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" spotless = "com.diffplug.spotless:7.0.2" From 16d7e49d474e37245125dfbadbd4ecb51b8aa64b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 20:53:54 -0500 Subject: [PATCH 286/941] Improve the error message for zip64 option (#1287) * Check e and e.cause both * Remove the comment about rethrowing * Remove DocumentationRegistry --- api/shadow.api | 2 +- lint-baseline.xml | 36 ++++--------------- .../plugins/shadow/tasks/ShadowCopyAction.kt | 20 +++++++---- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 4 --- 4 files changed, 21 insertions(+), 41 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 5570a339b..e8516032b 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -179,7 +179,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; - public fun (Ljava/io/File;Lkotlin/jvm/functions/Function1;Lorg/gradle/api/internal/DocumentationRegistry;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZLjava/lang/String;)V + public fun (Ljava/io/File;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZLjava/lang/String;)V public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; } diff --git a/lint-baseline.xml b/lint-baseline.xml index eca3ea723..d93d6837e 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -1,5 +1,5 @@ - + - - - - @@ -63,7 +52,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -74,7 +63,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -85,18 +74,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> - - - - @@ -107,7 +85,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +96,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 75c649e93..c245e4293 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -15,7 +15,6 @@ import org.apache.tools.zip.ZipEntry import org.apache.tools.zip.ZipOutputStream import org.gradle.api.GradleException import org.gradle.api.file.FileCopyDetails -import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.CopyActionProcessingStreamAction import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.internal.file.copy.CopyActionProcessingStream @@ -23,7 +22,6 @@ import org.gradle.api.internal.file.copy.FileCopyDetailsInternal import org.gradle.api.logging.Logging import org.gradle.api.tasks.WorkResult import org.gradle.api.tasks.WorkResults -import org.gradle.api.tasks.bundling.Zip import org.objectweb.asm.ClassReader import org.objectweb.asm.ClassWriter import org.objectweb.asm.commons.ClassRemapper @@ -34,7 +32,6 @@ import org.objectweb.asm.commons.ClassRemapper public open class ShadowCopyAction( private val zipFile: File, private val zosProvider: (File) -> ZipOutputStream, - private val documentationRegistry: DocumentationRegistry, private val transformers: Set, private val relocators: Set, private val unusedClasses: Set, @@ -58,14 +55,23 @@ public open class ShadowCopyAction( addDirs(zos) } } catch (e: Exception) { - if (e.cause is Zip64RequiredException) { + if (e is Zip64RequiredException || e.cause is Zip64RequiredException) { + val message = if (e is Zip64RequiredException) e.message else e.cause?.message throw Zip64RequiredException( - "${e.cause?.message}\n\nTo build this archive, please enable the zip64 extension.\n" + - "See: ${documentationRegistry.getDslRefForProperty(Zip::class.java, "zip64")}", + """ + $message + + To build this archive, please enable the zip64 extension. e.g. + ```kts + tasks.shadowJar { + isZip64 = true + } + ``` + See: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle.api.tasks.bundling.Zip:zip64 for more details. + """.trimIndent(), ) } zipFile.delete() - // Rethrow the exception like `java.util.zip.ZipException: archive is not a ZIP archive`. throw e } return WorkResults.didWork(true) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 59d16588d..7f945e28b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -12,7 +12,6 @@ import com.github.jengelman.gradle.plugins.shadow.internal.sourceSets import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator -import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer @@ -30,7 +29,6 @@ import org.gradle.api.UncheckedIOException import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DuplicatesStrategy -import org.gradle.api.internal.DocumentationRegistry import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.provider.Property @@ -269,7 +267,6 @@ public abstract class ShadowJar : throw UncheckedIOException("Unable to create ZIP output stream for file $destination.", e) } } - val documentationRegistry = services.get(DocumentationRegistry::class.java) val unusedClasses = if (minimizeJar.get()) { val unusedTracker = UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) includedDependencies.files.forEach { @@ -282,7 +279,6 @@ public abstract class ShadowJar : return ShadowCopyAction( archiveFile.get().asFile, zosProvider, - documentationRegistry, transformers.get(), relocators.get() + packageRelocators, unusedClasses, From 1bedb4c6a96bc10da71fd9504e640a24baef4c69 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 21:38:26 -0500 Subject: [PATCH 287/941] Remove Named from the parents of Transformer (#1289) It's unused. --- api/shadow.api | 7 +------ src/docs/changes/README.md | 4 ++++ .../gradle/plugins/shadow/transformers/Transformer.kt | 6 +----- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index e8516032b..edb8458fa 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -265,7 +265,6 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public fun getName ()Ljava/lang/String; public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V @@ -326,7 +325,6 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Compo public class com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public fun getName ()Ljava/lang/String; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun getResource ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z @@ -358,7 +356,6 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/IncludeReso public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getFile ()Lorg/gradle/api/file/RegularFileProperty; - public fun getName ()Ljava/lang/String; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun getResource ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z @@ -460,11 +457,10 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFile public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer : org/gradle/api/Named { +public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer$Companion; public abstract fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public static fun create (Ljava/lang/Class;Lorg/gradle/api/model/ObjectFactory;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer; - public fun getName ()Ljava/lang/String; public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public abstract fun hasTransformedResource ()Z public abstract fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V @@ -476,7 +472,6 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans } public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$DefaultImpls { - public static fun getName (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Ljava/lang/String; public static fun getObjectFactory (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lorg/gradle/api/model/ObjectFactory; } diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 6b9c854f4..856b31015 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -12,6 +12,10 @@ - Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) +**Removed** + +- **BREAKING CHANGE:** Remove `Named` from the parents of `Transformer`. ([#1289](https://github.com/GradleUp/shadow/pull/1289)) + ## [v9.0.0-beta9] (2025-02-24) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index e7f459e77..525e5a871 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import java.io.IOException import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.Named import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.Internal @@ -16,7 +15,7 @@ import org.gradle.api.tasks.Internal * @author John Engelman */ @JvmDefaultWithCompatibility -public interface Transformer : Named { +public interface Transformer { public fun canTransformResource(element: FileTreeElement): Boolean @Throws(IOException::class) @@ -27,9 +26,6 @@ public interface Transformer : Named { @Throws(IOException::class) public fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) - @Internal - override fun getName(): String = this::class.java.simpleName - /** * This is used for creating Gradle's lazy properties in the subclass, Shadow's build-in transformers that depend on * this have been injected via [ObjectFactory.newInstance]. Custom transformers should implement or inject From d9ce6a1da0ea73b6e74ee4ae33ff7efa76a86231 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 22:33:36 -0500 Subject: [PATCH 288/941] Move NoOpTransformer into the companion object of Transformer (#1290) We can get rid of an explicit public object. --- api/shadow.api | 14 +++++--------- .../shadow/caching/TransformerCachingTest.kt | 6 +++--- .../shadow/transformers/TransformersTest.kt | 4 ++-- .../ApacheLicenseResourceTransformer.kt | 2 +- .../DontIncludeResourceTransformer.kt | 2 +- .../transformers/IncludeResourceTransformer.kt | 2 +- .../plugins/shadow/transformers/Transformer.kt | 17 +++++++++-------- 7 files changed, 22 insertions(+), 25 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index edb8458fa..5ec729be0 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -394,14 +394,6 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestRes public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public final class com/github/jengelman/gradle/plugins/shadow/transformers/NoOpTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public static final field INSTANCE Lcom/github/jengelman/gradle/plugins/shadow/transformers/NoOpTransformer; - public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public fun hasTransformedResource ()Z - public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V -} - public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z @@ -467,8 +459,12 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/trans public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$Companion { +public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$Companion : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public final fun create (Ljava/lang/Class;Lorg/gradle/api/model/ObjectFactory;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$DefaultImpls { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 63b1b4ad0..4dbca5a53 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -12,9 +12,9 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.IncludeResourceTr import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestAppenderTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.NoOpTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.getContent @@ -146,8 +146,8 @@ class TransformerCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - // Use NoOpTransformer to mock a custom transformer here, it's not cacheable. - transform(${NoOpTransformer::class.java.name}.INSTANCE) + // Use Transformer.Companion (no-op) to mock a custom transformer here, it's not cacheable. + transform(${Transformer.Companion::class.java.name}) } """.trimIndent(), ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 62dca788f..c61cc69dc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -112,8 +112,8 @@ class TransformersTest : BaseTransformerTest() { implementation 'my:b:1.0' } $shadowJar { - // Use NoOpTransformer to mock a custom transformer here. - transform(${NoOpTransformer::class.java.name}.INSTANCE) + // Use Transformer.Companion (no-op) to mock a custom transformer here. + transform(${Transformer.Companion::class.java.name}) } """.trimIndent(), ) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index 4673b9074..c19a821c1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -10,7 +10,7 @@ import org.gradle.api.file.FileTreeElement * @author John Engelman */ @CacheableTransformer -public open class ApacheLicenseResourceTransformer : Transformer by NoOpTransformer { +public open class ApacheLicenseResourceTransformer : Transformer by Transformer.Companion { override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.path return LICENSE_PATH.equals(path, ignoreCase = true) || diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index fdb526108..ff92e665b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -18,7 +18,7 @@ import org.gradle.api.tasks.Optional @CacheableTransformer public open class DontIncludeResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer by NoOpTransformer { +) : Transformer by Transformer.Companion { @get:Optional @get:Input public open val resource: Property = objectFactory.property() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index c3f903b10..5bb7f09cc 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -22,7 +22,7 @@ import org.gradle.api.tasks.PathSensitivity @CacheableTransformer public open class IncludeResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer by NoOpTransformer { +) : Transformer by Transformer.Companion { @get:InputFile @get:PathSensitive(PathSensitivity.NONE) public open val file: RegularFileProperty = objectFactory.fileProperty() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt index 525e5a871..a54a6a523 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt @@ -35,7 +35,10 @@ public interface Transformer { public val objectFactory: ObjectFactory get() = throw NotImplementedError("You have to make sure this has been implemented or injected.") - public companion object { + /** + * This also implements [Transformer] but no-op, which means it could be used by Kotlin delegations. + */ + public companion object : Transformer { @JvmStatic public fun Class.create(objectFactory: ObjectFactory): T { // If the constructor takes a single ObjectFactory, inject it in. @@ -48,6 +51,11 @@ public interface Transformer { getDeclaredConstructor().newInstance() } } + + public override fun canTransformResource(element: FileTreeElement): Boolean = false + public override fun transform(context: TransformerContext): Unit = Unit + public override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean): Unit = Unit + public override fun hasTransformedResource(): Boolean = false } } @@ -61,10 +69,3 @@ public interface Transformer { @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS) public annotation class CacheableTransformer - -public object NoOpTransformer : Transformer { - public override fun canTransformResource(element: FileTreeElement): Boolean = false - public override fun transform(context: TransformerContext): Unit = Unit - public override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean): Unit = Unit - public override fun hasTransformedResource(): Boolean = false -} From b8b320b92a24d52395f2d52d9b64dd5d8738ca6a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Feb 2025 23:00:44 -0500 Subject: [PATCH 289/941] Rename Transformer to ResourceTransformer (#1288) Aims to better align with the name of [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java) and to distinguish itself from [org.gradle.api.Transformer.java](https://docs.gradle.org/current/javadoc/org/gradle/api/Transformer.html). --- api/shadow.api | 76 +++++++++---------- src/docs/changes/README.md | 5 ++ src/docs/configuration/merging/README.md | 18 ++--- .../gradle/plugins/shadow/BasePluginTest.kt | 4 +- .../shadow/caching/TransformerCachingTest.kt | 4 +- .../shadow/transformers/TransformersTest.kt | 2 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 4 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 14 ++-- .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 9 +-- .../ApacheLicenseResourceTransformer.kt | 2 +- .../ApacheNoticeResourceTransformer.kt | 2 +- .../transformers/AppendingTransformer.kt | 2 +- .../ComponentsXmlResourceTransformer.kt | 2 +- .../DontIncludeResourceTransformer.kt | 2 +- .../GroovyExtensionModuleTransformer.kt | 2 +- .../IncludeResourceTransformer.kt | 2 +- .../Log4j2PluginsCacheFileTransformer.kt | 2 +- .../ManifestAppenderTransformer.kt | 2 +- .../ManifestResourceTransformer.kt | 2 +- .../transformers/PropertiesFileTransformer.kt | 3 +- ...{Transformer.kt => ResourceTransformer.kt} | 10 +-- .../transformers/ServiceFileTransformer.kt | 2 +- .../transformers/XmlAppendingTransformer.kt | 2 +- .../transformers/BaseTransformerTest.kt | 8 +- 24 files changed, 92 insertions(+), 89 deletions(-) rename src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/{Transformer.kt => ResourceTransformer.kt} (86%) diff --git a/api/shadow.api b/api/shadow.api index 5ec729be0..3066ff87e 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -234,8 +234,8 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; + public synthetic fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; public synthetic fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; @@ -257,12 +257,12 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; public abstract fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; @@ -271,7 +271,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicen public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getAddHeader ()Lorg/gradle/api/provider/Property; @@ -290,7 +290,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNotic public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer$Companion; public static final field DEFAULT_SEPARATOR Ljava/lang/String; public fun (Lorg/gradle/api/model/ObjectFactory;)V @@ -309,7 +309,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Appen public abstract interface annotation class com/github/jengelman/gradle/plugins/shadow/transformers/CacheableTransformer : java/lang/annotation/Annotation { } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public static final field COMPONENTS_XML_PATH Ljava/lang/String; public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer$Companion; public fun ()V @@ -322,7 +322,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsX public final class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer$Companion { } -public class com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; @@ -332,7 +332,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/DontInclude public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer$Companion; public static final field KEY_EXTENSION_CLASSES Ljava/lang/String; public static final field KEY_MODULE_NAME Ljava/lang/String; @@ -352,7 +352,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExten public final class com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer$Companion { } -public class com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getFile ()Lorg/gradle/api/file/RegularFileProperty; @@ -363,7 +363,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/IncludeReso public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun hasTransformedResource ()Z @@ -371,7 +371,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2Plugi public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun append (Ljava/lang/String;Ljava/lang/Comparable;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer; public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z @@ -382,7 +382,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestApp public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun attributes (Ljava/util/Map;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer; public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z @@ -394,7 +394,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestRes public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getCharsetName ()Lorg/gradle/api/provider/Property; @@ -425,7 +425,29 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Prope public final fun from (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer, org/gradle/api/tasks/util/PatternFilterable { +public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer$Companion; + public abstract fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public static fun create (Ljava/lang/Class;Lorg/gradle/api/model/ObjectFactory;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer; + public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public abstract fun hasTransformedResource ()Z + public abstract fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer$Companion : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun create (Ljava/lang/Class;Lorg/gradle/api/model/ObjectFactory;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer$DefaultImpls { + public static fun getObjectFactory (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Lorg/gradle/api/model/ObjectFactory; +} + +public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer, org/gradle/api/tasks/util/PatternFilterable { public fun ()V public fun (Lorg/gradle/api/tasks/util/PatternSet;)V public synthetic fun (Lorg/gradle/api/tasks/util/PatternSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V @@ -449,28 +471,6 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFile public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer$Companion; - public abstract fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public static fun create (Ljava/lang/Class;Lorg/gradle/api/model/ObjectFactory;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer; - public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; - public abstract fun hasTransformedResource ()Z - public abstract fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V -} - -public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$Companion : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { - public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public final fun create (Ljava/lang/Class;Lorg/gradle/api/model/ObjectFactory;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer; - public fun hasTransformedResource ()Z - public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V -} - -public final class com/github/jengelman/gradle/plugins/shadow/transformers/Transformer$DefaultImpls { - public static fun getObjectFactory (Lcom/github/jengelman/gradle/plugins/shadow/transformers/Transformer;)Lorg/gradle/api/model/ObjectFactory; -} - public final class com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Companion; public fun (Ljava/lang/String;Ljava/io/InputStream;)V @@ -502,7 +502,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Trans public final fun builder ()Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext$Builder; } -public class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/Transformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getIgnoreDtd ()Lorg/gradle/api/provider/Property; diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 856b31015..9317bbf6f 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -8,6 +8,11 @@ - Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) You still need to manually configure `manifest.attributes` (e.g. `Main-Class` attr) in the `shadowJar` task if necessary. +**Changed** + +- **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) + Aims to better align with the name of [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java) and to distinguish itself from [org.gradle.api.Transformer.java](https://docs.gradle.org/current/javadoc/org/gradle/api/Transformer.html). + **Fixed** - Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) diff --git a/src/docs/configuration/merging/README.md b/src/docs/configuration/merging/README.md index b801f0785..9e6c69b85 100644 --- a/src/docs/configuration/merging/README.md +++ b/src/docs/configuration/merging/README.md @@ -9,14 +9,14 @@ This allows a [`Transformer`](https://gradleup.com/shadow/api/shadow/com.github. determine if it should process a particular entry and apply any modifications before writing the stream to the output. ```groovy -// Adding a Transformer -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +// Adding a ResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import javax.annotation.Nonnull import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement -class MyTransformer implements Transformer { +class MyTransformer implements ResourceTransformer { @Override boolean canTransformResource(@Nonnull FileTreeElement element) { return true } @@ -38,14 +38,14 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow Additionally, a `Transformer` can accept a `Closure` to configure the provided `Transformer`. ```groovy -// Configuring a Transformer -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +// Configuring a ResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import javax.annotation.Nonnull import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement -class MyTransformer implements Transformer { +class MyTransformer implements ResourceTransformer { boolean enabled @Override @@ -71,14 +71,14 @@ tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.Shadow An instantiated instance of a `Transformer` can also be provided. ```groovy -// Adding a Transformer Instance -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +// Adding a ResourceTransformer Instance +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import javax.annotation.Nonnull import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement -class MyTransformer implements Transformer { +class MyTransformer implements ResourceTransformer { final boolean enabled MyTransformer(boolean enabled) { 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 d814149fe..cfe77be8f 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 @@ -10,7 +10,7 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Compan import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarPath import java.io.Closeable @@ -390,7 +390,7 @@ abstract class BasePluginTest { return paths.joinToString(System.lineSeparator()) { "implementation files('${it.toUri().toURL().path}')" } } - inline fun transform( + inline fun transform( dependenciesBlock: String = "", transformerBlock: String = "", ): String { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 4dbca5a53..bab9fa101 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -13,8 +13,8 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCach import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestAppenderTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.getContent @@ -147,7 +147,7 @@ class TransformerCachingTest : BaseCachingTest() { """ $shadowJar { // Use Transformer.Companion (no-op) to mock a custom transformer here, it's not cacheable. - transform(${Transformer.Companion::class.java.name}) + transform(${ResourceTransformer.Companion::class.java.name}) } """.trimIndent(), ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index c61cc69dc..8d768fc85 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -113,7 +113,7 @@ class TransformersTest : BaseTransformerTest() { } $shadowJar { // Use Transformer.Companion (no-op) to mock a custom transformer here. - transform(${Transformer.Companion::class.java.name}) + transform(${ResourceTransformer.Companion::class.java.name}) } """.trimIndent(), ) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index c245e4293..9688a2c71 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -4,7 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper import com.github.jengelman.gradle.plugins.shadow.internal.cast import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import java.io.File import java.util.GregorianCalendar @@ -32,7 +32,7 @@ import org.objectweb.asm.commons.ClassRemapper public open class ShadowCopyAction( private val zipFile: File, private val zosProvider: (File) -> ZipOutputStream, - private val transformers: Set, + private val transformers: Set, private val relocators: Set, private val unusedClasses: Set, private val preserveFileTimestamps: Boolean, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 7f945e28b..f8f5ca1c0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -15,9 +15,9 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create import java.io.File import java.io.IOException import java.util.jar.JarFile @@ -102,7 +102,7 @@ public abstract class ShadowJar : } @get:Nested - public open val transformers: SetProperty = objectFactory.setProperty() + public open val transformers: SetProperty = objectFactory.setProperty() @get:Nested public open val relocators: SetProperty = objectFactory.setProperty() @@ -158,15 +158,15 @@ public abstract class ShadowJar : action?.execute(dependencyFilter.get()) } - override fun transform(clazz: Class): ShadowJar { + override fun transform(clazz: Class): ShadowJar { return transform(clazz, null) } - override fun transform(clazz: Class, action: Action?): ShadowJar = apply { + override fun transform(clazz: Class, action: Action?): ShadowJar = apply { addTransform(clazz.create(objectFactory), action) } - override fun transform(transformer: Transformer): ShadowJar = apply { + override fun transform(transformer: ResourceTransformer): ShadowJar = apply { addTransform(transformer, null) } @@ -292,7 +292,7 @@ public abstract class ShadowJar : relocators.add(relocator) } - private fun addTransform(transformer: T, action: Action?) { + private fun addTransform(transformer: T, action: Action?) { action?.execute(transformer) transformers.add(transformer) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt index b68176ac4..8d631fd65 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -2,9 +2,8 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator -import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer import java.lang.reflect.InvocationTargetException import org.gradle.api.Action import org.gradle.api.file.CopySpec @@ -22,7 +21,7 @@ public interface ShadowSpec : CopySpec { NoSuchMethodException::class, InvocationTargetException::class, ) - public fun transform(clazz: Class): ShadowSpec + public fun transform(clazz: Class): ShadowSpec @Throws( InstantiationException::class, @@ -30,9 +29,9 @@ public interface ShadowSpec : CopySpec { NoSuchMethodException::class, InvocationTargetException::class, ) - public fun transform(clazz: Class, action: Action?): ShadowSpec + public fun transform(clazz: Class, action: Action?): ShadowSpec - public fun transform(transformer: Transformer): ShadowSpec + public fun transform(transformer: ResourceTransformer): ShadowSpec public fun mergeServiceFiles(): ShadowSpec diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index c19a821c1..71f29c862 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -10,7 +10,7 @@ import org.gradle.api.file.FileTreeElement * @author John Engelman */ @CacheableTransformer -public open class ApacheLicenseResourceTransformer : Transformer by Transformer.Companion { +public open class ApacheLicenseResourceTransformer : ResourceTransformer by ResourceTransformer.Companion { override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.path return LICENSE_PATH.equals(path, ignoreCase = true) || diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 114651658..32ff2930f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -26,7 +26,7 @@ import org.gradle.api.tasks.Optional @CacheableTransformer public open class ApacheNoticeResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer { +) : ResourceTransformer { private val entries = mutableSetOf() private val organizationEntries = mutableMapOf>() private inline val charset get() = Charset.forName(charsetName.get()) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index 929d37bb9..878e44ab3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -22,7 +22,7 @@ import org.gradle.api.tasks.Optional @Suppress("ktlint:standard:backing-property-naming") public open class AppendingTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer { +) : ResourceTransformer { /** * Defer initialization, see [issue 763](https://github.com/GradleUp/shadow/issues/763). */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index 80041930d..b7c29ebb7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -22,7 +22,7 @@ import org.gradle.api.tasks.Internal * @author John Engelman */ @CacheableTransformer -public open class ComponentsXmlResourceTransformer : Transformer { +public open class ComponentsXmlResourceTransformer : ResourceTransformer { private val components = mutableMapOf() override fun canTransformResource(element: FileTreeElement): Boolean { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index ff92e665b..1b6f28cce 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -18,7 +18,7 @@ import org.gradle.api.tasks.Optional @CacheableTransformer public open class DontIncludeResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer by Transformer.Companion { +) : ResourceTransformer by ResourceTransformer.Companion { @get:Optional @get:Input public open val resource: Property = objectFactory.property() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index aacf8aadd..f34a1c38e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -25,7 +25,7 @@ import org.gradle.api.file.FileTreeElement * Related to [org.apache.maven.plugins.shade.resource.GroovyResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java). */ @CacheableTransformer -public open class GroovyExtensionModuleTransformer : Transformer { +public open class GroovyExtensionModuleTransformer : ResourceTransformer { private val module = Properties() /** diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index 5bb7f09cc..49e4a0b37 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -22,7 +22,7 @@ import org.gradle.api.tasks.PathSensitivity @CacheableTransformer public open class IncludeResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer by Transformer.Companion { +) : ResourceTransformer by ResourceTransformer.Companion { @get:InputFile @get:PathSensitive(PathSensitivity.NONE) public open val file: RegularFileProperty = objectFactory.fileProperty() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index 668c284aa..d36c1d1e2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -23,7 +23,7 @@ import org.gradle.api.file.FileTreeElement * @author John Engelman */ @CacheableTransformer -public open class Log4j2PluginsCacheFileTransformer : Transformer { +public open class Log4j2PluginsCacheFileTransformer : ResourceTransformer { /** * Log4j config files to share across the transformation stages. */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 32c9098f0..5643d0817 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -24,7 +24,7 @@ import org.gradle.api.tasks.Input @CacheableTransformer public open class ManifestAppenderTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer { +) : ResourceTransformer { private var manifestContents = ByteArray(0) @get:Input diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 9e03e330e..6a848f2b5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -31,7 +31,7 @@ import org.gradle.api.tasks.Optional @CacheableTransformer public open class ManifestResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer { +) : ResourceTransformer { private var manifestDiscovered = false private var manifest: Manifest? = null diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 0803bd1ae..4e6d6f377 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -6,7 +6,6 @@ import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry -import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import java.io.InputStream import java.nio.charset.Charset import java.util.Properties @@ -101,7 +100,7 @@ import org.gradle.api.tasks.Internal @CacheableTransformer public open class PropertiesFileTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer { +) : ResourceTransformer { private inline val charset get() = Charset.forName(charsetName.get()) @get:Internal diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt similarity index 86% rename from src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt index a54a6a523..134e4db9f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Transformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt @@ -15,7 +15,7 @@ import org.gradle.api.tasks.Internal * @author John Engelman */ @JvmDefaultWithCompatibility -public interface Transformer { +public interface ResourceTransformer { public fun canTransformResource(element: FileTreeElement): Boolean @Throws(IOException::class) @@ -36,11 +36,11 @@ public interface Transformer { get() = throw NotImplementedError("You have to make sure this has been implemented or injected.") /** - * This also implements [Transformer] but no-op, which means it could be used by Kotlin delegations. + * This also implements [ResourceTransformer] but no-op, which means it could be used by Kotlin delegations. */ - public companion object : Transformer { + public companion object : ResourceTransformer { @JvmStatic - public fun Class.create(objectFactory: ObjectFactory): T { + public fun Class.create(objectFactory: ObjectFactory): T { // If the constructor takes a single ObjectFactory, inject it in. val constructor = constructors.find { it.parameterTypes.singleOrNull() == ObjectFactory::class.java @@ -60,7 +60,7 @@ public interface Transformer { } /** - * Marks that a given instance of [Transformer] is compatible with the Gradle build cache. + * Marks that a given instance of [ResourceTransformer] is compatible with the Gradle build cache. * In other words, it has its appropriate inputs annotated so that Gradle can consider them when * determining the cache key. * diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index d5e9a54ec..8532c3ef4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -28,7 +28,7 @@ public open class ServiceFileTransformer( private val patternSet: PatternSet = PatternSet() .include(SERVICES_PATTERN) .exclude(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), -) : Transformer, +) : ResourceTransformer, PatternFilterable by patternSet { @get:Internal internal val serviceEntries = mutableMapOf>() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index ca7f6eabb..391a30056 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -30,7 +30,7 @@ import org.xml.sax.InputSource @CacheableTransformer public open class XmlAppendingTransformer @Inject constructor( final override val objectFactory: ObjectFactory, -) : Transformer { +) : ResourceTransformer { private var doc: Document? = null @get:Input diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 5645168e7..4cc33eb2c 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer.Companion.create +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.util.noOpDelegate import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import java.lang.reflect.ParameterizedType @@ -15,7 +15,7 @@ import org.gradle.api.file.FileTreeElement import org.gradle.api.file.RelativePath import org.junit.jupiter.api.BeforeEach -abstract class BaseTransformerTest { +abstract class BaseTransformerTest { protected lateinit var transformer: T private set @@ -32,7 +32,7 @@ abstract class BaseTransformerTest { protected companion object { const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" - fun Transformer.canTransformResource(path: String, isFile: Boolean = true): Boolean { + fun ResourceTransformer.canTransformResource(path: String, isFile: Boolean = true): Boolean { val element = object : FileTreeElement by noOpDelegate() { private val _relativePath = RelativePath.parse(isFile, path) override fun getPath(): String = _relativePath.pathString @@ -49,7 +49,7 @@ abstract class BaseTransformerTest { } fun doTransformAndGetTransformedPath( - transformer: Transformer, + transformer: ResourceTransformer, preserveFileTimestamps: Boolean, ): Path { val testableZipPath = createTempFile("testable-zip-file-", ".jar") From 5fefbab4fe54d5f34664e59833a3fe25a4fe312f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 27 Feb 2025 03:18:13 -0500 Subject: [PATCH 290/941] Migrate Apache links to https (#1291) --- LICENSE | 6 +++--- .../shadow/transformers/ApacheNoticeResourceTransformer.kt | 2 +- src/test/resources/components-1.xml | 3 +-- src/test/resources/components-2.xml | 3 +-- src/test/resources/components-expected.xml | 4 ++-- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/LICENSE b/LICENSE index 7a4a3ea24..62589edd1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ Apache License Version 2.0, January 2004 - http://www.apache.org/licenses/ + https://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -193,10 +193,10 @@ you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. \ No newline at end of file + limitations under the License. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 32ff2930f..409cb884f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -58,7 +58,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( public open val organizationName: Property = objectFactory.property("The Apache Software Foundation") @get:Input - public open val organizationURL: Property = objectFactory.property("http://www.apache.org/") + public open val organizationURL: Property = objectFactory.property("https://www.apache.org/") @get:Input public open val inceptionYear: Property = objectFactory.property("2006") diff --git a/src/test/resources/components-1.xml b/src/test/resources/components-1.xml index 352357336..b5b9cfecd 100644 --- a/src/test/resources/components-1.xml +++ b/src/test/resources/components-1.xml @@ -7,7 +7,7 @@ to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an @@ -45,4 +45,3 @@ under the License. - \ No newline at end of file diff --git a/src/test/resources/components-2.xml b/src/test/resources/components-2.xml index c5eff7145..b6b910ecc 100644 --- a/src/test/resources/components-2.xml +++ b/src/test/resources/components-2.xml @@ -7,7 +7,7 @@ to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an @@ -45,4 +45,3 @@ under the License. - \ No newline at end of file diff --git a/src/test/resources/components-expected.xml b/src/test/resources/components-expected.xml index 4b69cda5f..d1fc12c42 100644 --- a/src/test/resources/components-expected.xml +++ b/src/test/resources/components-expected.xml @@ -7,7 +7,7 @@ to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 + https://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an @@ -52,4 +52,4 @@ under the License. - \ No newline at end of file + From a1295f37eb4795b27a950a76017faae7f94173f1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 27 Feb 2025 04:18:46 -0500 Subject: [PATCH 291/941] Enable parallel flags in functional tests (#1292) * Enabling parallel configuration caching https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel * Enable parallel execution https://docs.gradle.org/current/userguide/performance.html#parallel_execution --- .../github/jengelman/gradle/plugins/shadow/BasePluginTest.kt | 3 +++ 1 file changed, 3 insertions(+) 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 cfe77be8f..1b7f907ba 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 @@ -381,7 +381,10 @@ abstract class BasePluginTest { "--warning-mode=fail", "--configuration-cache", "--build-cache", + "--parallel", "--stacktrace", + // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel + "-Dorg.gradle.configuration-cache.parallel=true", ) fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } From 96c7bc2f69691c4d0adc5a5697ebe3ea934ceac1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 3 Mar 2025 04:07:56 -0500 Subject: [PATCH 292/941] Correct functional test kits (#1297) * Move buildJar into super * Rename writeMainClass to writeClass * Replace main.class hardcode * Remove unnecessary inline functions * Rearrange properties * Mark AfterAll for doLast * Replace unnecessary project.files usages * Fix things --- .../plugins/shadow/ApplicationPluginTest.kt | 12 ++-- .../gradle/plugins/shadow/BasePluginTest.kt | 64 +++++++++++-------- .../gradle/plugins/shadow/JavaPluginTest.kt | 24 +++---- .../gradle/plugins/shadow/KmpPluginTest.kt | 2 +- .../gradle/plugins/shadow/RelocationTest.kt | 8 +-- .../shadow/caching/RelocationCachingTest.kt | 6 +- .../shadow/caching/ShadowJarCachingTest.kt | 28 ++++---- .../shadow/caching/TransformerCachingTest.kt | 12 ++-- .../transformers/BaseTransformerTest.kt | 4 -- .../ServiceFileTransformerTest.kt | 4 +- .../shadow/transformers/TransformersTest.kt | 6 +- 11 files changed, 90 insertions(+), 80 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 89b55aa7b..60df6330a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -31,6 +31,8 @@ import org.junit.jupiter.api.Test @ExperimentalPathApi class ApplicationPluginTest : BasePluginTest() { + private lateinit var mainClass: String + @Test fun integrationWithApplicationPluginAndJavaToolchains() { prepare( @@ -73,7 +75,7 @@ class ApplicationPluginTest : BasePluginTest() { commonAssertions( jarPath("myapp-shadow/lib/myapp-1.0-all.jar", installPath), - entriesContained = arrayOf("my/Main.class", *junitEntries), + entriesContained = arrayOf(mainClass, *junitEntries), ) val unixScript = path("myapp-shadow/bin/myapp", installPath) @@ -114,7 +116,7 @@ class ApplicationPluginTest : BasePluginTest() { ) @Test fun canOverrideMainClassAttrInManifestBlock() { - writeMainClass(className = "Main2") + val main2Class = writeClass(className = "Main2") prepare( projectBlock = """ shadowJar { @@ -135,7 +137,7 @@ class ApplicationPluginTest : BasePluginTest() { assertions(run(runShadowTask).output, "foo") commonAssertions( jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"), - entriesContained = entriesInA + arrayOf("my/Main.class", "my/Main2.class"), + entriesContained = entriesInA + arrayOf(mainClass, main2Class), mainClassAttr = "my.Main2", ) @@ -216,7 +218,7 @@ class ApplicationPluginTest : BasePluginTest() { dependenciesBlock: String = "implementation 'my:a:1.0'", runShadowBlock: String = "", ) { - writeMainClass(withImports = mainClassWithImports) + mainClass = writeClass(withImports = mainClassWithImports) projectScriptPath.appendText( """ apply plugin: 'application' @@ -244,7 +246,7 @@ class ApplicationPluginTest : BasePluginTest() { private fun commonAssertions( jarPath: JarPath, - entriesContained: Array = entriesInA + "my/Main.class", + entriesContained: Array = entriesInA + mainClass, mainClassAttr: String = "my.Main", classPathAttr: String? = null, ) { 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 1b7f907ba..8a1b285dd 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 @@ -12,6 +12,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository +import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder import com.github.jengelman.gradle.plugins.shadow.util.JarPath import java.io.Closeable import java.nio.file.Path @@ -32,6 +33,7 @@ import kotlin.io.path.writeText import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.AfterAll import org.junit.jupiter.api.AfterEach import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach @@ -50,6 +52,16 @@ abstract class BasePluginTest { lateinit var entriesInB: Array lateinit var entriesInAB: Array + val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" + val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" + val runShadowTask = ":$SHADOW_RUN_TASK_NAME" + val shadowDistZipTask = ":shadowDistZip" + + val projectScriptPath: Path get() = path("build.gradle") + val settingsScriptPath: Path get() = path("settings.gradle") + open val outputShadowJar: JarPath get() = jarPath("build/libs/my-1.0-all.jar") + val outputServerShadowJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") + @BeforeAll open fun doFirst() { localRepo = AppendableMavenRepository( @@ -87,21 +99,12 @@ abstract class BasePluginTest { println(projectScriptPath.readText()) } - @OptIn(ExperimentalPathApi::class) + @AfterAll fun doLast() { + @OptIn(ExperimentalPathApi::class) localRepo.root.deleteRecursively() } - val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" - val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" - val runShadowTask = ":$SHADOW_RUN_TASK_NAME" - val shadowDistZipTask = ":shadowDistZip" - - val projectScriptPath: Path get() = path("build.gradle") - val settingsScriptPath: Path get() = path("settings.gradle") - open val outputShadowJar: JarPath get() = jarPath("build/libs/my-1.0-all.jar") - val outputServerShadowJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") - fun getDefaultProjectBuildScript( plugin: String = "java", withGroup: Boolean = false, @@ -159,14 +162,18 @@ abstract class BasePluginTest { } } - inline fun run( + fun buildJar(relative: String, builder: JarBuilder.() -> Unit): Path { + return JarBuilder(path("temp/$relative")).apply(builder).write() + } + + fun run( vararg tasks: String, runnerBlock: (GradleRunner) -> GradleRunner = { it }, ): BuildResult { return runnerBlock(runner(tasks.toList())).build().assertNoDeprecationWarnings() } - inline fun runWithFailure( + fun runWithFailure( vararg tasks: String, runnerBlock: (GradleRunner) -> GradleRunner = { it }, ): BuildResult { @@ -189,17 +196,16 @@ abstract class BasePluginTest { }.publish() } - fun writeMainClass( + fun writeClass( sourceSet: String = "main", packageName: String = "my", withImports: Boolean = false, className: String = "Main", isJava: Boolean = true, - ): String { - 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( + classContent: () -> String = { + if (isJava) { + val imports = if (withImports) "import junit.framework.Test;" else "" + val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" """ package $packageName; $imports @@ -211,12 +217,10 @@ abstract class BasePluginTest { System.out.println($classRef); } } - """.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( + """.trimIndent() + } else { + val imports = if (withImports) "import junit.framework.Test;" else "" + val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" """ package $packageName $imports @@ -226,8 +230,14 @@ abstract class BasePluginTest { println(content) println($classRef) } - """.trimIndent(), - ) + """.trimIndent() + } + }, + ): String { + if (isJava) { + path("src/$sourceSet/java/$packageName/$className.java").writeText(classContent()) + } else { + path("src/$sourceSet/kotlin/$packageName/$className.kt").writeText(classContent()) } val baseClassPath = packageName.replace('.', '/') + "/$className" return if (isJava) "$baseClassPath.class" else "${baseClassPath}Kt.class" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 832ee671b..c96dadf5a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -73,7 +73,7 @@ class JavaPluginTest : BasePluginTest() { disabledReason = "Gradle 8.3 doesn't support Java 21.", ) fun compatibleWithMinGradleVersion() { - writeMainClass(withImports = true) + val mainClass = writeClass(withImports = true) projectScriptPath.appendText( """ dependencies { @@ -88,7 +88,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - "my/Main.class", + mainClass, *junitEntries, ) } @@ -505,7 +505,7 @@ class JavaPluginTest : BasePluginTest() { ) @Test fun canRegisterCustomShadowJarTask() { - writeMainClass(sourceSet = "test", withImports = true) + val mainClass = writeClass(sourceSet = "test", withImports = true) val testShadowJarTask = "testShadowJar" projectScriptPath.appendText( """ @@ -530,7 +530,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(result).taskOutcomeEquals(":$testShadowJarTask", SUCCESS) assertThat(jarPath("build/libs/my-1.0-tests.jar")).useAll { containsEntries( - "my/Main.class", + mainClass, *junitEntries, ) getMainAttr(mainClassAttributeKey).isNotNull() @@ -586,12 +586,12 @@ class JavaPluginTest : BasePluginTest() { fun failBuildIfProcessingBadJar() { val badJarPath = path("bad.jar").apply { writeText("A bad jar.") - }.toUri().toURL().path + } projectScriptPath.appendText( """ dependencies { - implementation files('$badJarPath') + ${implementationFiles(badJarPath)} } """.trimIndent(), ) @@ -606,7 +606,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun worksWithArchiveFileName() { - writeMainClass() + val mainClass = writeClass() projectScriptPath.appendText( """ dependencies { @@ -622,7 +622,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(jarPath("build/libs/my-shadow.tar")).useAll { containsEntries( - "my/Main.class", + mainClass, *junitEntries, ) } @@ -659,12 +659,12 @@ class JavaPluginTest : BasePluginTest() { @Test fun canAddExtraFilesIntoShadowJar() { - writeMainClass() + val mainClass = writeClass() path("Foo").writeText("Foo") projectScriptPath.appendText( """ $shadowJar { - from(files('${artifactAJar.toUri().toURL().path}')) { + from(file('${artifactAJar.toUri().toURL().path}')) { into('META-INF') } from('Foo') { @@ -678,7 +678,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - "my/Main.class", + mainClass, "META-INF/a-1.0.jar", "Bar/Foo", ) @@ -693,7 +693,7 @@ class JavaPluginTest : BasePluginTest() { } assertThat(jarPath(unzipped.name)).useAll { containsEntries(*entriesInA) - doesNotContainEntries("my/Main.class") + doesNotContainEntries(mainClass) } } } 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 0cd58c3ce..fc728c446 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,7 +21,7 @@ class KmpPluginTest : BasePluginTest() { @Test fun compatKmpJvmTarget() { - val mainClass = writeMainClass(sourceSet = "jvmMain", isJava = false) + val mainClass = writeClass(sourceSet = "jvmMain", isJava = false) projectScriptPath.appendText( """ kotlin { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index b56da6009..6a0f6177b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -26,7 +26,7 @@ class RelocationTest : BasePluginTest() { @ParameterizedTest @MethodSource("prefixProvider") fun autoRelocation(relocationPrefix: String) { - val mainClass = writeMainClass() + val mainClass = writeClass() projectScriptPath.appendText( """ dependencies { @@ -63,7 +63,7 @@ class RelocationTest : BasePluginTest() { ) @Test fun relocateDependencyFiles() { - val mainClass = writeMainClass() + val mainClass = writeClass() projectScriptPath.appendText( """ dependencies { @@ -104,7 +104,7 @@ class RelocationTest : BasePluginTest() { @Test fun relocateDependencyFilesWithFiltering() { - val mainClass = writeMainClass() + val mainClass = writeClass() projectScriptPath.appendText( """ dependencies { @@ -355,7 +355,7 @@ class RelocationTest : BasePluginTest() { // Minus 3 sec to avoid the time difference between the file system and the JVM. val currentTimeMillis = System.currentTimeMillis() - 3.seconds.inWholeMilliseconds val junitEntryTimeRange = junitRawEntries.map { it.time }.let { it.min()..it.max() } - writeMainClass(withImports = true) + writeClass(withImports = true) projectScriptPath.appendText( """ dependencies { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index fcead73b7..24e9af75e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -18,11 +18,11 @@ class RelocationCachingTest : BaseCachingTest() { } """.trimIndent() + System.lineSeparator(), ) - writeMainClass(withImports = true) + val mainClass = writeClass(withImports = true) assertCompositeExecutions { containsEntries( - "my/Main.class", + mainClass, *junitEntries, ) } @@ -39,7 +39,7 @@ class RelocationCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - "my/Main.class", + mainClass, *shadowedEntries, ) doesNotContainEntries( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index eff308c11..6bf229a75 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -73,8 +73,8 @@ class ShadowJarCachingTest : BaseCachingTest() { ) @Test fun shadowJarIsCachedCorrectlyWhenUsingIncludesExcludes() { - writeMainClass(className = "Main") - writeMainClass(className = "Main2") + val mainClass = writeClass(className = "Main") + val main2Class = writeClass(className = "Main2") projectScriptPath.appendText( """ dependencies { @@ -85,7 +85,7 @@ class ShadowJarCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - val entries = entriesInAB + arrayOf("my/Main.class", "my/Main2.class") + val entries = entriesInAB + arrayOf(mainClass, main2Class) containsEntries(*entries) } @@ -99,8 +99,8 @@ class ShadowJarCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - "my/Main.class", - "my/Main2.class", + mainClass, + main2Class, ) doesNotContainEntries(*entriesInAB) } @@ -108,17 +108,17 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - include 'my/Main.class' + include '$mainClass' } """.trimIndent() + System.lineSeparator(), ) assertCompositeExecutions { containsEntries( - "my/Main.class", + mainClass, ) doesNotContainEntries( - "my/Main2.class", + main2Class, "a.properties", "a2.properties", "b.properties", @@ -128,15 +128,15 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - include 'my/Main2.class' + include '$main2Class' } """.trimIndent() + System.lineSeparator(), ) assertCompositeExecutions { containsEntries( - "my/Main.class", - "my/Main2.class", + mainClass, + main2Class, ) doesNotContainEntries(*entriesInAB) } @@ -144,7 +144,7 @@ class ShadowJarCachingTest : BaseCachingTest() { @Test fun shadowJarIsCachedCorrectlyWhenUsingDependencyIncludesExcludes() { - writeMainClass(withImports = true) + val mainClass = writeClass(withImports = true) projectScriptPath.appendText( """ dependencies { @@ -155,7 +155,7 @@ class ShadowJarCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - "my/Main.class", + mainClass, *junitEntries, ) } @@ -172,7 +172,7 @@ class ShadowJarCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - "my/Main.class", + mainClass, ) doesNotContainEntries( *junitEntries, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index bab9fa101..a2da85a07 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -29,17 +29,19 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource class TransformerCachingTest : BaseCachingTest() { + private lateinit var mainClass: String + @BeforeEach override fun setup() { super.setup() - writeMainClass() + mainClass = writeClass() } @Test fun shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer() { val assertions = { assertCompositeExecutions { - containsEntries("my/Main.class") + containsEntries(mainClass) } } @@ -66,7 +68,7 @@ class TransformerCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.properties").writeText("foo=bar") val assertions = { name: String -> assertCompositeExecutions { - containsEntries("my/Main.class", "foo/$name.properties") + containsEntries(mainClass, "foo/$name.properties") getContent("foo/$name.properties").isEqualTo("foo=$name") } } @@ -96,7 +98,7 @@ class TransformerCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.xml").writeText("bar") val assertions = { name: String -> assertCompositeExecutions { - containsEntries("my/Main.class", "foo/$name.xml") + containsEntries(mainClass, "foo/$name.xml") getContent("foo/$name.xml").contains("$name") } } @@ -167,7 +169,7 @@ class TransformerCachingTest : BaseCachingTest() { } val assertions = { assertCompositeExecutions { - containsEntries("my/Main.class") + containsEntries(mainClass) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index b4ee8444b..ea4a545ed 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -23,10 +23,6 @@ abstract class BaseTransformerTest : BasePluginTest() { return buildJar("two.jar", builder) } - inline fun buildJar(relative: String, builder: JarBuilder.() -> Unit): Path { - return JarBuilder(path("temp/$relative")).apply(builder).write() - } - protected companion object { const val CONTENT_ONE = "one # NOTE: No newline terminates this line/file" const val CONTENT_TWO = "two # NOTE: No newline terminates this line/file" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 13aabc361..e0bb1c682 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -170,7 +170,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { val servicesBarEntry = "META-INF/services/foo.Bar" val one = buildJarOne { insert(servicesBarEntry, CONTENT_ONE) - }.toUri().toURL().path + } localRepo.module("foo", "bar", "1.0") { buildJar { insert(servicesBarEntry, CONTENT_TWO) @@ -181,7 +181,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """ dependencies { implementation 'foo:bar:1.0' - implementation files('$one') + ${implementationFiles(one)} } $shadowJar { mergeServiceFiles() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 8d768fc85..f54237cc6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -23,7 +23,7 @@ import org.junit.jupiter.params.provider.MethodSource class TransformersTest : BaseTransformerTest() { @Test fun manifestRetained() { - writeMainClass() + writeClass() projectScriptPath.appendText( """ jar { @@ -45,7 +45,7 @@ class TransformersTest : BaseTransformerTest() { @Test fun manifestTransformed() { - writeMainClass() + writeClass() projectScriptPath.appendText(MANIFEST_ATTRS) @@ -59,7 +59,7 @@ class TransformersTest : BaseTransformerTest() { ) @Test fun shadowManifestLeaksToJarManifest() { - writeMainClass() + writeClass() projectScriptPath.appendText(MANIFEST_ATTRS) run("jar", shadowJarTask) From b00e9fdbc90bb9708cf181053c335aea069dd1ad Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 3 Mar 2025 04:34:46 -0500 Subject: [PATCH 293/941] Add a test for issue 520 (#1298) --- .../gradle/plugins/shadow/JavaPluginTest.kt | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index c96dadf5a..a5fb73587 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -7,6 +7,7 @@ import assertk.assertions.containsMatch import assertk.assertions.isEqualTo import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty +import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull import assertk.assertions.isNull import assertk.assertions.isTrue @@ -696,4 +697,45 @@ class JavaPluginTest : BasePluginTest() { doesNotContainEntries(mainClass) } } + + @Issue( + "https://github.com/GradleUp/shadow/issues/520", + ) + @Test + fun onlyKeepFilesFromProjectWhenDuplicatesStrategyIsExclude() { + val fooJar = buildJar("foo.jar") { + insert("module-info.class", "module myModuleName {}") + } + val mainClass = writeClass() + writeClass(className = "module-info") { + "module myModuleName {}" + } + projectScriptPath.appendText( + """ + dependencies { + ${implementationFiles(fooJar)} + } + $shadowJar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + excludes.remove( + 'module-info.class' + ) + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries( + mainClass, + "module-info.class", + ) + getContent("module-info.class").all { + isNotEmpty() + // It's the compiled class instead of the original content. + isNotEqualTo("module myModuleName {}") + } + } + } } From b16142865c73c523aa391c31843a6bc5344b5cb5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 3 Mar 2025 04:41:50 -0500 Subject: [PATCH 294/941] Note the exclusion of module-info.class (#1299) --- src/docs/getting-started/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/docs/getting-started/README.md b/src/docs/getting-started/README.md index ddfdebddb..c651a3632 100644 --- a/src/docs/getting-started/README.md +++ b/src/docs/getting-started/README.md @@ -76,6 +76,8 @@ following behavior: * `META-INF/*.SF` * `META-INF/*.DSA` * `META-INF/*.RSA` + * `META-INF/versions/**/module-info.class` + * `module-info.class` * Creates and registers the `shadow` component in the project (used for integrating with `maven-publish`). ## Shadowing Gradle Plugins From 347c254bd8e9ae6fa63ae1aa02ebda759934a20b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 3 Mar 2025 05:07:43 -0500 Subject: [PATCH 295/941] Configure gemini pref (#1300) --- .gemini/config.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .gemini/config.yaml diff --git a/.gemini/config.yaml b/.gemini/config.yaml new file mode 100644 index 000000000..2c3813212 --- /dev/null +++ b/.gemini/config.yaml @@ -0,0 +1,9 @@ +have_fun: true +code_review: + disable: true + comment_severity_threshold: MEDIUM + max_review_comments: -1 + pull_request_opened: + help: false + summary: false + code_review: true From 39653ac4406c57f4671b566cfabb629821756290 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 3 Mar 2025 05:17:06 -0500 Subject: [PATCH 296/941] Tweak doesNotContainEntries for canAddExtraFilesIntoShadowJar (#1301) --- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index a5fb73587..1e7666abf 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -683,7 +683,11 @@ class JavaPluginTest : BasePluginTest() { "META-INF/a-1.0.jar", "Bar/Foo", ) - doesNotContainEntries(*entriesInA) + doesNotContainEntries( + *entriesInA, + "Foo", + "Foo/", + ) getContent("Bar/Foo").isEqualTo("Foo") } val unzipped = path("unzipped") From 63abc8c5b1fd37a0c5eb9ad69f61c87ab3d5398f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 4 Mar 2025 01:42:15 -0500 Subject: [PATCH 297/941] Mark DefaultInheritManifest as internal (#1303) --- api/shadow.api | 18 ------------------ lint-baseline.xml | 16 ++++++++-------- src/docs/changes/README.md | 1 + .../DefaultInheritManifest.kt | 5 +++-- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 1 + 5 files changed, 13 insertions(+), 28 deletions(-) rename src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/{tasks => internal}/DefaultInheritManifest.kt (87%) diff --git a/api/shadow.api b/api/shadow.api index 3066ff87e..60f2346d9 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -143,24 +143,6 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public fun relocatePath-bvWaKNU (Ljava/lang/String;)Ljava/lang/String; } -public class com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest : com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest, org/gradle/api/java/archives/Manifest { - public fun (Lorg/gradle/api/internal/file/FileResolver;)V - public fun (Lorg/gradle/api/internal/file/FileResolver;Lorg/gradle/api/java/archives/internal/DefaultManifest;)V - public synthetic fun (Lorg/gradle/api/internal/file/FileResolver;Lorg/gradle/api/java/archives/internal/DefaultManifest;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun attributes (Ljava/util/Map;)Lorg/gradle/api/java/archives/Manifest; - public fun attributes (Ljava/util/Map;Ljava/lang/String;)Lorg/gradle/api/java/archives/Manifest; - public fun from (Ljava/lang/Object;Lgroovy/lang/Closure;)Lorg/gradle/api/java/archives/Manifest; - public fun from (Ljava/lang/Object;Lorg/gradle/api/Action;)Lorg/gradle/api/java/archives/Manifest; - public fun from ([Ljava/lang/Object;)Lorg/gradle/api/java/archives/Manifest; - public fun getAttributes ()Lorg/gradle/api/java/archives/Attributes; - public synthetic fun getEffectiveManifest ()Lorg/gradle/api/java/archives/Manifest; - public fun getEffectiveManifest ()Lorg/gradle/api/java/archives/internal/DefaultManifest; - public fun getSections ()Ljava/util/Map; - public fun inheritFrom ([Ljava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; - public fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; - public fun writeTo (Ljava/lang/Object;)Lorg/gradle/api/java/archives/Manifest; -} - public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter : java/io/Serializable { public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; diff --git a/lint-baseline.xml b/lint-baseline.xml index d93d6837e..7ab8f759e 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -7,8 +7,8 @@ errorLine1="import org.gradle.api.internal.file.FileResolver" errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -18,8 +18,8 @@ errorLine1="import org.gradle.api.java.archives.internal.DefaultManifest" errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -29,8 +29,8 @@ errorLine1="import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec" errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -85,7 +85,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -96,7 +96,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/docs/changes/README.md b/src/docs/changes/README.md index 9317bbf6f..4c982cde0 100644 --- a/src/docs/changes/README.md +++ b/src/docs/changes/README.md @@ -12,6 +12,7 @@ - **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) Aims to better align with the name of [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java) and to distinguish itself from [org.gradle.api.Transformer.java](https://docs.gradle.org/current/javadoc/org/gradle/api/Transformer.html). +- **BREAKING CHANGE:** Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) **Fixed** diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt similarity index 87% rename from src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt index bb33441bb..3a3e21254 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DefaultInheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt @@ -1,12 +1,13 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks +package com.github.jengelman.gradle.plugins.shadow.internal +import com.github.jengelman.gradle.plugins.shadow.tasks.InheritManifest import org.gradle.api.Action import org.gradle.api.internal.file.FileResolver import org.gradle.api.java.archives.Manifest import org.gradle.api.java.archives.internal.DefaultManifest import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec -public open class DefaultInheritManifest @JvmOverloads constructor( +internal class DefaultInheritManifest @JvmOverloads constructor( private val fileResolver: FileResolver, private val internalManifest: DefaultManifest = DefaultManifest(fileResolver), ) : InheritManifest, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index f8f5ca1c0..0432bd136 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter +import com.github.jengelman.gradle.plugins.shadow.internal.DefaultInheritManifest import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.fileCollection From 6e5c50f56e62cc55c0edcaf172afa0d785419c25 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 09:26:00 +0800 Subject: [PATCH 298/941] Update plugin android-lint to v8.9.0 (#1304) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dfb606e9c..aab04fb66 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,6 +34,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.8.2" +android-lint = "com.android.lint:8.9.0" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" spotless = "com.diffplug.spotless:7.0.2" From d590ff83c8f3369201d08ee7315fe1051c1b6ae5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 4 Mar 2025 22:59:57 -0500 Subject: [PATCH 299/941] Update doc code blocks and tests (#1305) * Replace `groovy no-run` blocks * Update shell block * Comment --- src/docs/getting-started/README.md | 14 +++++++------- src/docs/introduction/README.md | 4 ++-- .../plugins/shadow/executor/GroovyBuildExecutor.kt | 13 +++++++++---- .../plugins/shadow/fixture/GroovyDslFixture.kt | 3 ++- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/docs/getting-started/README.md b/src/docs/getting-started/README.md index c651a3632..7067b632d 100644 --- a/src/docs/getting-started/README.md +++ b/src/docs/getting-started/README.md @@ -1,21 +1,21 @@ # Getting Started -```groovy no-run +```groovy plugins { id 'java' - id 'com.gradleup.shadow' version '@version@' + id 'com.gradleup.shadow' version '' } ``` Alternatively, the plugin can be added to the buildscript classpath and applied: -```groovy no-run +```groovy buildscript { repositories { gradlePluginPortal() } dependencies { - classpath 'com.gradleup.shadow:shadow-gradle-plugin:@version@' + classpath 'com.gradleup.shadow:shadow-gradle-plugin:' } } @@ -31,14 +31,14 @@ Sonatype's snapshots repository.

-```groovy no-run +```groovy buildscript { repositories { mavenCentral() - maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' } + maven { url = 'https://oss.sonatype.org/content/repositories/snapshots/' } } dependencies { - classpath 'com.gradleup.shadow:shadow-gradle-plugin:' + classpath 'com.gradleup.shadow:shadow-gradle-plugin:' } } diff --git a/src/docs/introduction/README.md b/src/docs/introduction/README.md index e02a3504a..21060564c 100644 --- a/src/docs/introduction/README.md +++ b/src/docs/introduction/README.md @@ -26,8 +26,8 @@ It must be available on the target system. Executable JARs contain a JAR MANIFEST that specifies the application Main Class. This allows the application to be started with a single command: -```bash -$ java -jar application-shadow.jar +```shell +java -jar application-shadow.jar ``` ### Library Bundling diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt index aac341f25..d5958cd5b 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt @@ -36,11 +36,16 @@ class GroovyBuildExecutor( val mainScript = buildString { append(imports) append(System.lineSeparator()) - if (!snippetWithoutImports.contains("plugins {")) { - append(fixture.pluginsBlock) - append(System.lineSeparator()) + // All buildscript {} blocks must appear before any plugins {} blocks in the script. + if (snippetWithoutImports.contains("buildscript {")) { + append(snippetWithoutImports) + } else { + if (!snippetWithoutImports.contains("plugins {")) { + append(fixture.pluginsBlock) + append(System.lineSeparator()) + } + append(snippetWithoutImports) } - append(snippetWithoutImports) append(System.lineSeparator()) }.trimIndent() tempDir.addSubProject("main", mainScript) diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt index aef8bc174..5cc82e008 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt @@ -20,7 +20,8 @@ object GroovyDslFixture : SnippetFixture { listOf( imports.toString(), - scriptMinusImports.toString(), + // Replace the version placeholders. + scriptMinusImports.toString().replace("", "+"), ) } } From d080748a56727a3bee46bf79689762cce52e01f5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 5 Mar 2025 00:35:05 -0500 Subject: [PATCH 300/941] Migrate doc sites to MkDocs (#1302) * Delete package.json and yarn.lock * Move docs folder out of src * Run `mkdocs new .` https://squidfunk.github.io/mkdocs-material * Update mkdocs.yml and nav items * Delete .vuepress things * Disable the explicit home page * Remove gitPublish and node plugins * Inline releaseAll task * Add a job for testing deploying * Update dokka output dir * Tweak the deployment step * Update release workflow * Update ignore list * Update changelog * Add Dokka output into delete list --- .github/pull_request_template.md | 2 +- .github/workflows/release.yml | 21 +- .gitignore | 5 +- README.md | 2 +- RELEASING.md | 2 +- build-logic/build.gradle.kts | 2 - .../shadow.convention.deploy.gradle.kts | 30 - .../shadow.convention.publish.gradle.kts | 4 + build.gradle.kts | 18 +- docs/README.md | 16 + {src/docs => docs}/about/README.md | 0 .../application-plugin/README.md | 0 {src/docs => docs}/changes/README.md | 1 + {src/docs => docs}/configuration/README.md | 0 .../configuration/dependencies/README.md | 0 .../configuration/filtering/README.md | 0 .../configuration/merging/README.md | 0 .../configuration/minimizing/README.md | 0 .../configuration/relocation/README.md | 0 .../reproducible-builds/README.md | 0 {src/docs => docs}/custom-tasks/README.md | 0 {src/docs => docs}/getting-started/README.md | 0 .../public => docs/images}/logo+type.orig.svg | 220 +- .../public => docs/images}/logo+type.svg | 0 .../public => docs/images}/logo.orig.svg | 128 +- .../.vuepress/public => docs/images}/logo.svg | 0 {src/docs => docs}/introduction/README.md | 0 {src/docs => docs}/kmp-plugin/README.md | 0 {src/docs => docs}/multi-project/README.md | 0 {src/docs => docs}/plugins/README.md | 0 {src/docs => docs}/publishing/README.md | 0 gradle/libs.versions.toml | 2 - mkdocs.yml | 79 + package.json | 16 - src/docs/.vuepress/components/ApiLink.vue | 13 - src/docs/.vuepress/config.js | 37 - src/docs/.vuepress/override.styl | 3 - src/docs/.vuepress/style.styl | 9 - src/docs/README.md | 16 - yarn.lock | 8798 ----------------- 40 files changed, 290 insertions(+), 9134 deletions(-) delete mode 100644 build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts create mode 100644 docs/README.md rename {src/docs => docs}/about/README.md (100%) rename {src/docs => docs}/application-plugin/README.md (100%) rename {src/docs => docs}/changes/README.md (99%) rename {src/docs => docs}/configuration/README.md (100%) rename {src/docs => docs}/configuration/dependencies/README.md (100%) rename {src/docs => docs}/configuration/filtering/README.md (100%) rename {src/docs => docs}/configuration/merging/README.md (100%) rename {src/docs => docs}/configuration/minimizing/README.md (100%) rename {src/docs => docs}/configuration/relocation/README.md (100%) rename {src/docs => docs}/configuration/reproducible-builds/README.md (100%) rename {src/docs => docs}/custom-tasks/README.md (100%) rename {src/docs => docs}/getting-started/README.md (100%) rename {src/docs/.vuepress/public => docs/images}/logo+type.orig.svg (98%) rename {src/docs/.vuepress/public => docs/images}/logo+type.svg (100%) rename {src/docs/.vuepress/public => docs/images}/logo.orig.svg (97%) rename {src/docs/.vuepress/public => docs/images}/logo.svg (100%) rename {src/docs => docs}/introduction/README.md (100%) rename {src/docs => docs}/kmp-plugin/README.md (100%) rename {src/docs => docs}/multi-project/README.md (100%) rename {src/docs => docs}/plugins/README.md (100%) rename {src/docs => docs}/publishing/README.md (100%) create mode 100644 mkdocs.yml delete mode 100644 package.json delete mode 100644 src/docs/.vuepress/components/ApiLink.vue delete mode 100644 src/docs/.vuepress/config.js delete mode 100644 src/docs/.vuepress/override.styl delete mode 100644 src/docs/.vuepress/style.styl delete mode 100644 src/docs/README.md delete mode 100644 yarn.lock diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 7d08589f5..8fd407013 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,3 +1,3 @@ --- -- [ ] [CHANGELOG](https://github.com/GradleUp/shadow/blob/main/src/docs/changes/README.md)'s "Unreleased" section has been updated, if applicable. +- [ ] [CHANGELOG](https://github.com/GradleUp/shadow/blob/main/docs/changes/README.md)'s "Unreleased" section has been updated, if applicable. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a3e9d3ca1..0a30bacb0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,31 +20,26 @@ jobs: - uses: gradle/actions/setup-gradle@v4 with: cache-read-only: true - - uses: actions/setup-node@v4 - with: - # Due to some limitations of https://github.com/node-gradle/gradle-node-plugin. - node-version: '16' - # Used for gitPublish tasks. - - name: Set up Git - run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' # Disable CC due to https://github.com/gradle/gradle/issues/22779 - - run: ./gradlew releaseAll --no-configuration-cache + - run: ./gradlew publish publishPlugins dokkaHtml --no-configuration-cache env: GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_KEY }} GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_SECRET }} - GITHUB_USER: ${{ github.repository_owner }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} + - name: Deploy site + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + pip install mkdocs-material + mkdocs gh-deploy --force - name: Extract release notes # TODO: replace this after https://github.com/ffurrer2/extract-release-notes/pull/355 is merged. uses: Goooler/extract-release-notes@6e686e7a607d03716b7cff561371a82065b22c33 with: - changelog_file: src/docs/changes/README.md + changelog_file: docs/changes/README.md release_notes_file: RELEASE_NOTES.md - name: Create release run: gh release create ${{ github.ref_name }} --notes-file RELEASE_NOTES.md diff --git a/.gitignore b/.gitignore index 03ace8838..11f395974 100644 --- a/.gitignore +++ b/.gitignore @@ -10,11 +10,10 @@ out .gradletasknamecache .gradle-test-kit classes/ -node_modules/ -yarn-error.log -src/docs/.vuepress/dist/ .DS_Store jd-gui.cfg bin/ .vscode/ .kotlin +# Generated by Dokka, it's depended on by MkDocs. +docs/api/ diff --git a/README.md b/README.md index b9d8266c9..e9b8e7e06 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. ## Documentation - [User Guide](https://gradleup.com/shadow/) -- [CHANGELOG](src/docs/changes/README.md) +- [CHANGELOG](docs/changes/README.md) ## Current Status diff --git a/RELEASING.md b/RELEASING.md index 0c3a226e9..186bc6728 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -2,7 +2,7 @@ 1. Update the `VERSION_NAME` in `gradle.properties` to the release version. -2. Update the [changelog](src/docs/changes/README.md): +2. Update the [changelog](docs/changes/README.md): 1. Change the `Unreleased` header to the release version. 2. Add a link URL to ensure the header link works. 3. Add a new `Unreleased` section to the top. diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts index c2c603c1e..84ff20fd0 100644 --- a/build-logic/build.gradle.kts +++ b/build-logic/build.gradle.kts @@ -5,7 +5,5 @@ plugins { dependencies { implementation(libs.pluginPublish) implementation(libs.mavenPublish) - implementation(libs.gitPublish) - implementation(libs.node) implementation(libs.jetbrains.dokka) } diff --git a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts deleted file mode 100644 index 986d56d57..000000000 --- a/build-logic/src/main/kotlin/shadow.convention.deploy.gradle.kts +++ /dev/null @@ -1,30 +0,0 @@ -import org.apache.tools.ant.filters.ReplaceTokens - -plugins { - id("org.ajoberstar.git-publish") - id("com.github.node-gradle.node") -} - -val yarnBuild = tasks.named("yarn_build") { - dependsOn(tasks.yarn) - inputs.dir("src/docs") - outputs.dir("build/site") -} - -gitPublish { - repoUri = "https://github.com/GradleUp/shadow.git" - branch = "gh-pages" - username = providers.environmentVariable("GITHUB_USER") - password = providers.environmentVariable("GITHUB_TOKEN") - contents { - from(yarnBuild) - from(tasks.named("dokkaHtml")) { - into("api") - } - filter( - "tokens" to mapOf( - "version" to version, - ), - ) - } -} diff --git a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts index 750dda2c2..1e960054e 100644 --- a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts @@ -28,6 +28,10 @@ gradlePlugin { } } +tasks.dokkaHtml { + outputDirectory = rootDir.resolve("docs/api") +} + tasks.publishPlugins { doFirst { if (version.toString().endsWith("SNAPSHOT")) { diff --git a/build.gradle.kts b/build.gradle.kts index 24b04fc1c..a5c640076 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,7 +9,6 @@ plugins { alias(libs.plugins.jetbrains.bcv) alias(libs.plugins.spotless) id("shadow.convention.publish") - id("shadow.convention.deploy") } java { @@ -77,8 +76,8 @@ testing.suites { register("integrationTest") { targets.configureEach { testTask { - val docsDir = file("src/docs") - // Add src/docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. + val docsDir = file("docs") + // Add docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. inputs.dir(docsDir) systemProperty("DOCS_DIR", docsDir.absolutePath) } @@ -176,16 +175,5 @@ tasks.clean { val dirs = includedBuilds.map { it.projectDir } + projectDir delete.addAll(dirs.map { it.resolve(".gradle") }) delete.addAll(dirs.map { it.resolve(".kotlin") }) - delete.add("node_modules") -} - -tasks.register("releaseAll") { - description = "Publishes the plugin to maven repos and deploys website." - group = PublishingPlugin.PUBLISH_TASK_GROUP - - dependsOn( - tasks.publish, - tasks.publishPlugins, - tasks.gitPublishPush, - ) + delete.add(tasks.dokkaHtml.map { it.outputDirectory }) } diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..7ddf3c213 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,16 @@ +

+ Gradle Shadow Plugin +

Shadow Gradle Plugin

+

The library author's dependency toolkit

+
+ +--- + +Previously this plugin was developed by [@johnrengelman](https://github.com/johnrengelman) and published under the ID [ +`com.github.johnrengelman.shadow`](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) +before maintenance was transferred to the [GradleUp organization](https://github.com/GradleUp) to ensure future +development, see [#908](https://github.com/GradleUp/shadow/issues/908). + +If you are still using the old plugin ID in your build script, we recommend to switch to the new plugin ID [ +`com.gradleup.shadow`](https://plugins.gradle.org/plugin/com.gradleup.shadow) +and update to the latest version to receive all the latest bug fixes and improvements. diff --git a/src/docs/about/README.md b/docs/about/README.md similarity index 100% rename from src/docs/about/README.md rename to docs/about/README.md diff --git a/src/docs/application-plugin/README.md b/docs/application-plugin/README.md similarity index 100% rename from src/docs/application-plugin/README.md rename to docs/application-plugin/README.md diff --git a/src/docs/changes/README.md b/docs/changes/README.md similarity index 99% rename from src/docs/changes/README.md rename to docs/changes/README.md index 4c982cde0..db2671a55 100644 --- a/src/docs/changes/README.md +++ b/docs/changes/README.md @@ -13,6 +13,7 @@ - **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) Aims to better align with the name of [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java) and to distinguish itself from [org.gradle.api.Transformer.java](https://docs.gradle.org/current/javadoc/org/gradle/api/Transformer.html). - **BREAKING CHANGE:** Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) +- Migrate doc sites to MkDocs. ([#1302](https://github.com/GradleUp/shadow/pull/1302)) **Fixed** diff --git a/src/docs/configuration/README.md b/docs/configuration/README.md similarity index 100% rename from src/docs/configuration/README.md rename to docs/configuration/README.md diff --git a/src/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md similarity index 100% rename from src/docs/configuration/dependencies/README.md rename to docs/configuration/dependencies/README.md diff --git a/src/docs/configuration/filtering/README.md b/docs/configuration/filtering/README.md similarity index 100% rename from src/docs/configuration/filtering/README.md rename to docs/configuration/filtering/README.md diff --git a/src/docs/configuration/merging/README.md b/docs/configuration/merging/README.md similarity index 100% rename from src/docs/configuration/merging/README.md rename to docs/configuration/merging/README.md diff --git a/src/docs/configuration/minimizing/README.md b/docs/configuration/minimizing/README.md similarity index 100% rename from src/docs/configuration/minimizing/README.md rename to docs/configuration/minimizing/README.md diff --git a/src/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md similarity index 100% rename from src/docs/configuration/relocation/README.md rename to docs/configuration/relocation/README.md diff --git a/src/docs/configuration/reproducible-builds/README.md b/docs/configuration/reproducible-builds/README.md similarity index 100% rename from src/docs/configuration/reproducible-builds/README.md rename to docs/configuration/reproducible-builds/README.md diff --git a/src/docs/custom-tasks/README.md b/docs/custom-tasks/README.md similarity index 100% rename from src/docs/custom-tasks/README.md rename to docs/custom-tasks/README.md diff --git a/src/docs/getting-started/README.md b/docs/getting-started/README.md similarity index 100% rename from src/docs/getting-started/README.md rename to docs/getting-started/README.md diff --git a/src/docs/.vuepress/public/logo+type.orig.svg b/docs/images/logo+type.orig.svg similarity index 98% rename from src/docs/.vuepress/public/logo+type.orig.svg rename to docs/images/logo+type.orig.svg index b1d3e39d0..730bdff84 100644 --- a/src/docs/.vuepress/public/logo+type.orig.svg +++ b/docs/images/logo+type.orig.svg @@ -1,110 +1,110 @@ - - - - - - + + + + + + diff --git a/src/docs/.vuepress/public/logo+type.svg b/docs/images/logo+type.svg similarity index 100% rename from src/docs/.vuepress/public/logo+type.svg rename to docs/images/logo+type.svg diff --git a/src/docs/.vuepress/public/logo.orig.svg b/docs/images/logo.orig.svg similarity index 97% rename from src/docs/.vuepress/public/logo.orig.svg rename to docs/images/logo.orig.svg index 41debcfdd..20f1be3e9 100644 --- a/src/docs/.vuepress/public/logo.orig.svg +++ b/docs/images/logo.orig.svg @@ -1,64 +1,64 @@ - - - - - - + + + + + + diff --git a/src/docs/.vuepress/public/logo.svg b/docs/images/logo.svg similarity index 100% rename from src/docs/.vuepress/public/logo.svg rename to docs/images/logo.svg diff --git a/src/docs/introduction/README.md b/docs/introduction/README.md similarity index 100% rename from src/docs/introduction/README.md rename to docs/introduction/README.md diff --git a/src/docs/kmp-plugin/README.md b/docs/kmp-plugin/README.md similarity index 100% rename from src/docs/kmp-plugin/README.md rename to docs/kmp-plugin/README.md diff --git a/src/docs/multi-project/README.md b/docs/multi-project/README.md similarity index 100% rename from src/docs/multi-project/README.md rename to docs/multi-project/README.md diff --git a/src/docs/plugins/README.md b/docs/plugins/README.md similarity index 100% rename from src/docs/plugins/README.md rename to docs/plugins/README.md diff --git a/src/docs/publishing/README.md b/docs/publishing/README.md similarity index 100% rename from src/docs/publishing/README.md rename to docs/publishing/README.md diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index aab04fb66..a2cea5e65 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,9 +19,7 @@ moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "mosh 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" } diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 000000000..073e93647 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,79 @@ +# pip install mkdocs mkdocs-material +# mkdocs serve +# mkdocs gh-deploy + +site_name: Shadow Gradle Plugin +repo_name: Shadow +repo_url: https://github.com/GradleUp/shadow +site_description: "Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries" +site_author: GradleUp developers +remote_branch: gh-pages + +theme: + name: 'material' + favicon: images/logo.orig.svg + logo: images/logo.svg + palette: + - media: '(prefers-color-scheme: light)' + scheme: default + primary: 'white' + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: '(prefers-color-scheme: dark)' + scheme: slate + primary: 'black' + toggle: + icon: material/brightness-4 + name: Switch to light mode + font: + text: 'Lato' + code: 'Fira Code' + features: + - content.code.copy + - content.code.select + +markdown_extensions: + - smarty + - codehilite: + guess_lang: false + - footnotes + - meta + - toc: + permalink: true + - pymdownx.betterem: + smart_enable: all + - pymdownx.caret + - pymdownx.inlinehilite + - pymdownx.magiclink + - pymdownx.smartsymbols + - pymdownx.superfences + - pymdownx.emoji + - pymdownx.tabbed: + alternate_style: true + - tables + - admonition + - attr_list + - md_in_html + +nav: + - 'Home': README.md + - 'Introduction': introduction/README.md + - 'Getting Started': getting-started/README.md + - 'Configuration': + - 'Overview': configuration/README.md + - 'Filtering': configuration/filtering/README.md + - 'Dependencies': configuration/dependencies/README.md + - 'Merging': configuration/merging/README.md + - 'Relocation': configuration/relocation/README.md + - 'Minimizing': configuration/minimizing/README.md + - 'Reproducible Builds': configuration/reproducible-builds/README.md + - 'Custom Tasks': custom-tasks/README.md + - 'Application Plugin': application-plugin/README.md + - 'KMP Plugin': kmp-plugin/README.md + - 'Publishing': publishing/README.md + - 'Multi-Project': multi-project/README.md + - 'Plugins': plugins/README.md + - 'Changes': changes/README.md + - 'API': api/index.html + - 'About': about/README.md diff --git a/package.json b/package.json deleted file mode 100644 index 9275a85aa..000000000 --- a/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "shadow", - "version": "1.0.0", - "main": "index.js", - "repository": "git@github.com:GradleUp/shadow.git", - "author": "GradleUp developers", - "license": "MIT", - "devDependencies": { - "prismjs": "^1.20.0", - "vuepress": "^1.5.0" - }, - "scripts": { - "build": "vuepress build src/docs", - "start": "vuepress dev src/docs" - } -} diff --git a/src/docs/.vuepress/components/ApiLink.vue b/src/docs/.vuepress/components/ApiLink.vue deleted file mode 100644 index 0eee4ead7..000000000 --- a/src/docs/.vuepress/components/ApiLink.vue +++ /dev/null @@ -1,13 +0,0 @@ - - - diff --git a/src/docs/.vuepress/config.js b/src/docs/.vuepress/config.js deleted file mode 100644 index 0d1794345..000000000 --- a/src/docs/.vuepress/config.js +++ /dev/null @@ -1,37 +0,0 @@ -module.exports = { - base: "/shadow/", - dest: "build/site", - ga: "UA-321220-4", - title: 'Gradle Shadow Plugin', - themeConfig: { - repo: "GradleUp/shadow", - docsBranch: 'main', - editLinks: true, - editLinkText: 'Help improve these docs!', - logo: '/logo+type.svg', - docsDir: 'src/docs', - nav: [ - { text: 'User Guide', link: '/introduction/' } - ], - sidebar: [ - '/', - '/introduction/', - '/getting-started/', - '/configuration/', - '/configuration/filtering/', - '/configuration/dependencies/', - '/configuration/merging/', - '/configuration/relocation/', - '/configuration/minimizing/', - '/configuration/reproducible-builds/', - '/custom-tasks/', - '/application-plugin/', - '/kmp-plugin/', - '/publishing/', - '/multi-project/', - '/plugins/', - '/changes/', - '/about/' - ] - } -} diff --git a/src/docs/.vuepress/override.styl b/src/docs/.vuepress/override.styl deleted file mode 100644 index c4f85f6b5..000000000 --- a/src/docs/.vuepress/override.styl +++ /dev/null @@ -1,3 +0,0 @@ -$accentColor = #B66E3C -$textColor = #5F3416 -$borderColor = #9FB2CC diff --git a/src/docs/.vuepress/style.styl b/src/docs/.vuepress/style.styl deleted file mode 100644 index 5b6988268..000000000 --- a/src/docs/.vuepress/style.styl +++ /dev/null @@ -1,9 +0,0 @@ -@import "~prismjs/themes/prism-tomorrow.css" - -div[class~="language-groovy"] - &:before - content "groovy" - -div[class~="language-kotlin"] - &:before - content "kotlin" diff --git a/src/docs/README.md b/src/docs/README.md deleted file mode 100644 index d2ccdc0d6..000000000 --- a/src/docs/README.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -home: true -heroText: Gradle Shadow Plugin -heroImage: /logo.svg -tagline: The library author's dependency toolkit -actionText: User Guide → -actionLink: /introduction/ ---- - -[API Docs](https://gradleup.com/shadow/api/index.html) - -Previously this plugin was developed by [@johnrengelman](https://github.com/johnrengelman) and published under the ID [`com.github.johnrengelman.shadow`](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) -before maintenance was transferred to the [GradleUp organization](https://github.com/GradleUp) to ensure future development, see [#908](https://github.com/GradleUp/shadow/issues/908). - -If you are still using the old plugin ID in your build script, we recommend to switch to the new plugin ID [`com.gradleup.shadow`](https://plugins.gradle.org/plugin/com.gradleup.shadow) -and update to the latest version to receive all the latest bug fixes and improvements. diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index a9c15b28f..000000000 --- a/yarn.lock +++ /dev/null @@ -1,8798 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@ampproject/remapping@^2.2.0": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.1.tgz#99e8e11851128b8702cd57c33684f1d0f260b630" - integrity sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg== - dependencies: - "@jridgewell/gen-mapping" "^0.3.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" - integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== - dependencies: - "@babel/highlight" "^7.22.10" - chalk "^2.4.2" - -"@babel/code-frame@^7.25.9", "@babel/code-frame@^7.26.2": - version "7.26.2" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" - integrity sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ== - dependencies: - "@babel/helper-validator-identifier" "^7.25.9" - js-tokens "^4.0.0" - picocolors "^1.0.0" - -"@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.9.tgz#71cdb00a1ce3a329ce4cbec3a44f9fef35669730" - integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== - -"@babel/core@^7.11.0", "@babel/core@^7.8.4": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" - integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== - dependencies: - "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.10" - "@babel/generator" "^7.22.10" - "@babel/helper-compilation-targets" "^7.22.10" - "@babel/helper-module-transforms" "^7.22.9" - "@babel/helpers" "^7.22.10" - "@babel/parser" "^7.22.10" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.2" - semver "^6.3.1" - -"@babel/generator@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" - integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== - dependencies: - "@babel/types" "^7.22.10" - "@jridgewell/gen-mapping" "^0.3.2" - "@jridgewell/trace-mapping" "^0.3.17" - jsesc "^2.5.1" - -"@babel/generator@^7.26.5": - version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.26.5.tgz#e44d4ab3176bbcaf78a5725da5f1dc28802a9458" - integrity sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw== - dependencies: - "@babel/parser" "^7.26.5" - "@babel/types" "^7.26.5" - "@jridgewell/gen-mapping" "^0.3.5" - "@jridgewell/trace-mapping" "^0.3.25" - jsesc "^3.0.2" - -"@babel/helper-annotate-as-pure@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" - integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz#573e735937e99ea75ea30788b57eb52fab7468c9" - integrity sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ== - dependencies: - "@babel/types" "^7.22.10" - -"@babel/helper-compilation-targets@^7.22.10", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.9.6": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024" - integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q== - dependencies: - "@babel/compat-data" "^7.22.9" - "@babel/helper-validator-option" "^7.22.5" - browserslist "^4.21.9" - lru-cache "^5.1.1" - semver "^6.3.1" - -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.10", "@babel/helper-create-class-features-plugin@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3" - integrity sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - semver "^6.3.1" - -"@babel/helper-create-regexp-features-plugin@^7.18.6", "@babel/helper-create-regexp-features-plugin@^7.22.5": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" - integrity sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - regexpu-core "^5.3.1" - semver "^6.3.1" - -"@babel/helper-define-polyfill-provider@^0.4.2": - version "0.4.2" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.4.2.tgz#82c825cadeeeee7aad237618ebbe8fa1710015d7" - integrity sha512-k0qnnOqHn5dK9pZpfD5XXZ9SojAITdCKRn2Lp6rnDGzIbaP0rHyMPk/4wsSxVBVz4RfN0q6VpXWP2pDGIoQ7hw== - dependencies: - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-plugin-utils" "^7.22.5" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - -"@babel/helper-environment-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" - integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== - -"@babel/helper-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" - integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== - dependencies: - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" - -"@babel/helper-hoist-variables@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" - integrity sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-member-expression-to-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz#0a7c56117cad3372fbf8d2fb4bf8f8d64a1e76b2" - integrity sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-module-imports@^7.0.0", "@babel/helper-module-imports@^7.22.5", "@babel/helper-module-imports@^7.8.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" - integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-module-transforms@^7.22.5", "@babel/helper-module-transforms@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz#92dfcb1fbbb2bc62529024f72d942a8c97142129" - integrity sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ== - dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" - "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/helper-validator-identifier" "^7.22.5" - -"@babel/helper-optimise-call-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz#f21531a9ccbff644fdd156b4077c16ff0c3f609e" - integrity sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" - integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== - -"@babel/helper-remap-async-to-generator@^7.22.5", "@babel/helper-remap-async-to-generator@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82" - integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-wrap-function" "^7.22.9" - -"@babel/helper-replace-supers@^7.22.5", "@babel/helper-replace-supers@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.22.9.tgz#cbdc27d6d8d18cd22c81ae4293765a5d9afd0779" - integrity sha512-LJIKvvpgPOPUThdYqcX6IXRuIcTkcAub0IaDRGCZH0p5GPUp7PhRU9QVgFcDDd51BaPkk77ZjqFwh6DZTAEmGg== - dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-member-expression-to-functions" "^7.22.5" - "@babel/helper-optimise-call-expression" "^7.22.5" - -"@babel/helper-simple-access@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" - integrity sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-skip-transparent-expression-wrappers@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz#007f15240b5751c537c40e77abb4e89eeaaa8847" - integrity sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-split-export-declaration@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" - integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== - -"@babel/helper-string-parser@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" - integrity sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA== - -"@babel/helper-validator-identifier@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" - integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== - -"@babel/helper-validator-identifier@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz#24b64e2c3ec7cd3b3c547729b8d16871f22cbdc7" - integrity sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ== - -"@babel/helper-validator-option@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" - integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== - -"@babel/helper-wrap-function@^7.22.9": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz#d845e043880ed0b8c18bd194a12005cb16d2f614" - integrity sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ== - dependencies: - "@babel/helper-function-name" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.10" - -"@babel/helpers@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" - integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== - dependencies: - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.10" - "@babel/types" "^7.22.10" - -"@babel/highlight@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" - integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== - dependencies: - "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.4.2" - js-tokens "^4.0.0" - -"@babel/parser@^7.18.4", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" - integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== - -"@babel/parser@^7.25.9", "@babel/parser@^7.26.5", "@babel/parser@^7.26.7": - version "7.26.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.26.7.tgz#e114cd099e5f7d17b05368678da0fb9f69b3385c" - integrity sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w== - dependencies: - "@babel/types" "^7.26.7" - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz#87245a21cd69a73b0b81bcda98d443d6df08f05e" - integrity sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.22.5.tgz#fef09f9499b1f1c930da8a0c419db42167d792ca" - integrity sha512-31Bb65aZaUwqCbWMnZPduIZxCBngHFlzyN6Dq6KAJjtx+lx6ohKHubc61OomYi7XwVD4Ol0XCVz4h+pYFR048g== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.5" - -"@babel/plugin-proposal-class-properties@^7.8.3": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz#b110f59741895f7ec21a6fff696ec46265c446a3" - integrity sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-proposal-decorators@^7.8.3": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.22.10.tgz#d6a8c3a9018e1b13e6647f869c5ea56ff2b585d4" - integrity sha512-KxN6TqZzcFi4uD3UifqXElBTBNLAEH1l3vzMQj6JwJZbL2sZlThxSViOKCYY+4Ah4V4JhQ95IVB7s/Y6SJSlMQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.10" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.9" - "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/plugin-syntax-decorators" "^7.22.10" - -"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": - version "7.21.0-placeholder-for-preset-env.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" - integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-class-static-block@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz#195df89b146b4b78b3bf897fd7a257c84659d406" - integrity sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-decorators@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.22.10.tgz#7d83ea04d893c442b78ebf4c3cbac59a7211deff" - integrity sha512-z1KTVemBjnz+kSEilAsI4lbkPOl5TvJH7YDSY1CTIzvLWJ+KHXp+mRe8VPmfnyvqOPqar1V2gid2PleKzRUstQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-import-assertions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.22.5.tgz#07d252e2aa0bc6125567f742cd58619cb14dce98" - integrity sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-import-attributes@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.22.5.tgz#ab840248d834410b829f569f5262b9e517555ecb" - integrity sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-import-meta@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.2.0", "@babel/plugin-syntax-jsx@^7.22.5", "@babel/plugin-syntax-jsx@^7.8.3": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" - integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-private-property-in-object@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz#0dc6671ec0ea22b6e94a1114f857970cd39de1ad" - integrity sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-top-level-await@^7.14.5": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" - integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-arrow-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.22.5.tgz#e5ba566d0c58a5b2ba2a8b795450641950b71958" - integrity sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-async-generator-functions@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz#45946cd17f915b10e65c29b8ed18a0a50fc648c8" - integrity sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g== - dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.9" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-transform-async-to-generator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.22.5.tgz#c7a85f44e46f8952f6d27fe57c2ed3cc084c3775" - integrity sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ== - dependencies: - "@babel/helper-module-imports" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.5" - -"@babel/plugin-transform-block-scoped-functions@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.22.5.tgz#27978075bfaeb9fa586d3cb63a3d30c1de580024" - integrity sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-block-scoping@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz#88a1dccc3383899eb5e660534a76a22ecee64faa" - integrity sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-class-properties@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.22.5.tgz#97a56e31ad8c9dc06a0b3710ce7803d5a48cca77" - integrity sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-class-static-block@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.22.5.tgz#3e40c46f048403472d6f4183116d5e46b1bff5ba" - integrity sha512-SPToJ5eYZLxlnp1UzdARpOGeC2GbHvr9d/UV0EukuVx8atktg194oe+C5BqQ8jRTkgLRVOPYeXRSBg1IlMoVRA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - -"@babel/plugin-transform-classes@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.22.6.tgz#e04d7d804ed5b8501311293d1a0e6d43e94c3363" - integrity sha512-58EgM6nuPNG6Py4Z3zSuu0xWu2VfodiMi72Jt5Kj2FECmaYk1RrTXA45z6KBFsu9tRgwQDwIiY4FXTt+YsSFAQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-optimise-call-expression" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.6" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.22.5.tgz#cd1e994bf9f316bd1c2dafcd02063ec261bb3869" - integrity sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/template" "^7.22.5" - -"@babel/plugin-transform-destructuring@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz#38e2273814a58c810b6c34ea293be4973c4eb5e2" - integrity sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-dotall-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" - integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-duplicate-keys@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.22.5.tgz#b6e6428d9416f5f0bba19c70d1e6e7e0b88ab285" - integrity sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-dynamic-import@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.22.5.tgz#d6908a8916a810468c4edff73b5b75bda6ad393e" - integrity sha512-0MC3ppTB1AMxd8fXjSrbPa7LT9hrImt+/fcj+Pg5YMD7UQyWp/02+JWpdnCymmsXwIx5Z+sYn1bwCn4ZJNvhqQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-transform-exponentiation-operator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.22.5.tgz#402432ad544a1f9a480da865fda26be653e48f6a" - integrity sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-export-namespace-from@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.22.5.tgz#57c41cb1d0613d22f548fddd8b288eedb9973a5b" - integrity sha512-X4hhm7FRnPgd4nDA4b/5V280xCx6oL7Oob5+9qVS5C13Zq4bh1qq7LU0GgRU6b5dBWBvhGaXYVB4AcN6+ol6vg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-transform-for-of@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.22.5.tgz#ab1b8a200a8f990137aff9a084f8de4099ab173f" - integrity sha512-3kxQjX1dU9uudwSshyLeEipvrLjBCVthCgeTp6CzE/9JYrlAIaeekVxRpCWsDDfYTfRZRoCeZatCQvwo+wvK8A== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.22.5.tgz#935189af68b01898e0d6d99658db6b164205c143" - integrity sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg== - dependencies: - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-json-strings@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.22.5.tgz#14b64352fdf7e1f737eed68de1a1468bd2a77ec0" - integrity sha512-DuCRB7fu8MyTLbEQd1ew3R85nx/88yMoqo2uPSjevMj3yoN7CDM8jkgrY0wmVxfJZyJ/B9fE1iq7EQppWQmR5A== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-transform-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.22.5.tgz#e9341f4b5a167952576e23db8d435849b1dd7920" - integrity sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-logical-assignment-operators@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.22.5.tgz#66ae5f068fd5a9a5dc570df16f56c2a8462a9d6c" - integrity sha512-MQQOUW1KL8X0cDWfbwYP+TbVbZm16QmQXJQ+vndPtH/BoO0lOKpVoEDMI7+PskYxH+IiE0tS8xZye0qr1lGzSA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-transform-member-expression-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.22.5.tgz#4fcc9050eded981a468347dd374539ed3e058def" - integrity sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-modules-amd@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.22.5.tgz#4e045f55dcf98afd00f85691a68fc0780704f526" - integrity sha512-R+PTfLTcYEmb1+kK7FNkhQ1gP4KgjpSO6HfH9+f8/yfp2Nt3ggBjiVpRwmwTlfqZLafYKJACy36yDXlEmI9HjQ== - dependencies: - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-modules-commonjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.22.5.tgz#7d9875908d19b8c0536085af7b053fd5bd651bfa" - integrity sha512-B4pzOXj+ONRmuaQTg05b3y/4DuFz3WcCNAXPLb2Q0GT0TrGKGxNKV4jwsXts+StaM0LQczZbOpj8o1DLPDJIiA== - dependencies: - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-simple-access" "^7.22.5" - -"@babel/plugin-transform-modules-systemjs@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.22.5.tgz#18c31410b5e579a0092638f95c896c2a98a5d496" - integrity sha512-emtEpoaTMsOs6Tzz+nbmcePl6AKVtS1yC4YNAeMun9U8YCsgadPNxnOPQ8GhHFB2qdx+LZu9LgoC0Lthuu05DQ== - dependencies: - "@babel/helper-hoist-variables" "^7.22.5" - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" - -"@babel/plugin-transform-modules-umd@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.22.5.tgz#4694ae40a87b1745e3775b6a7fe96400315d4f98" - integrity sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ== - dependencies: - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.22.5.tgz#67fe18ee8ce02d57c855185e27e3dc959b2e991f" - integrity sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-new-target@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.22.5.tgz#1b248acea54ce44ea06dfd37247ba089fcf9758d" - integrity sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-nullish-coalescing-operator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.22.5.tgz#f8872c65776e0b552e0849d7596cddd416c3e381" - integrity sha512-6CF8g6z1dNYZ/VXok5uYkkBBICHZPiGEl7oDnAx2Mt1hlHVHOSIKWJaXHjQJA5VB43KZnXZDIexMchY4y2PGdA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-transform-numeric-separator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.22.5.tgz#57226a2ed9e512b9b446517ab6fa2d17abb83f58" - integrity sha512-NbslED1/6M+sXiwwtcAB/nieypGw02Ejf4KtDeMkCEpP6gWFMX1wI9WKYua+4oBneCCEmulOkRpwywypVZzs/g== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-transform-object-rest-spread@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.22.5.tgz#9686dc3447df4753b0b2a2fae7e8bc33cdc1f2e1" - integrity sha512-Kk3lyDmEslH9DnvCDA1s1kkd3YWQITiBOHngOtDL9Pt6BZjzqb6hiOlb8VfjiiQJ2unmegBqZu0rx5RxJb5vmQ== - dependencies: - "@babel/compat-data" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.22.5" - -"@babel/plugin-transform-object-super@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.22.5.tgz#794a8d2fcb5d0835af722173c1a9d704f44e218c" - integrity sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-replace-supers" "^7.22.5" - -"@babel/plugin-transform-optional-catch-binding@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.22.5.tgz#842080be3076703be0eaf32ead6ac8174edee333" - integrity sha512-pH8orJahy+hzZje5b8e2QIlBWQvGpelS76C63Z+jhZKsmzfNaPQ+LaW6dcJ9bxTpo1mtXbgHwy765Ro3jftmUg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-transform-optional-chaining@^7.22.10", "@babel/plugin-transform-optional-chaining@^7.22.5": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz#076d28a7e074392e840d4ae587d83445bac0372a" - integrity sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-transform-parameters@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.22.5.tgz#c3542dd3c39b42c8069936e48717a8d179d63a18" - integrity sha512-AVkFUBurORBREOmHRKo06FjHYgjrabpdqRSwq6+C7R5iTCZOsM4QbcB27St0a4U6fffyAOqh3s/qEfybAhfivg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-private-methods@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.22.5.tgz#21c8af791f76674420a147ae62e9935d790f8722" - integrity sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-private-property-in-object@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.22.5.tgz#07a77f28cbb251546a43d175a1dda4cf3ef83e32" - integrity sha512-/9xnaTTJcVoBtSSmrVyhtSvO3kbqS2ODoh2juEU72c3aYonNF0OMGiaz2gjukyKM2wBBYJP38S4JiE0Wfb5VMQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - -"@babel/plugin-transform-property-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.22.5.tgz#b5ddabd73a4f7f26cd0e20f5db48290b88732766" - integrity sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-regenerator@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz#8ceef3bd7375c4db7652878b0241b2be5d0c3cca" - integrity sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - regenerator-transform "^0.15.2" - -"@babel/plugin-transform-reserved-words@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.22.5.tgz#832cd35b81c287c4bcd09ce03e22199641f964fb" - integrity sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-runtime@^7.11.0": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.10.tgz#89eda6daf1d3af6f36fb368766553054c8d7cd46" - integrity sha512-RchI7HePu1eu0CYNKHHHQdfenZcM4nz8rew5B1VWqeRKdcwW5aQ5HeG9eTUbWiAS1UrmHVLmoxTWHt3iLD/NhA== - dependencies: - "@babel/helper-module-imports" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.5" - babel-plugin-polyfill-corejs3 "^0.8.3" - babel-plugin-polyfill-regenerator "^0.5.2" - semver "^6.3.1" - -"@babel/plugin-transform-shorthand-properties@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.22.5.tgz#6e277654be82b5559fc4b9f58088507c24f0c624" - integrity sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-spread@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.22.5.tgz#6487fd29f229c95e284ba6c98d65eafb893fea6b" - integrity sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" - -"@babel/plugin-transform-sticky-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.22.5.tgz#295aba1595bfc8197abd02eae5fc288c0deb26aa" - integrity sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-template-literals@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.22.5.tgz#8f38cf291e5f7a8e60e9f733193f0bcc10909bff" - integrity sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-typeof-symbol@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.22.5.tgz#5e2ba478da4b603af8673ff7c54f75a97b716b34" - integrity sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-escapes@^7.22.10": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz#c723f380f40a2b2f57a62df24c9005834c8616d9" - integrity sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg== - dependencies: - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-property-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.22.5.tgz#098898f74d5c1e86660dc112057b2d11227f1c81" - integrity sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.22.5.tgz#ce7e7bb3ef208c4ff67e02a22816656256d7a183" - integrity sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/plugin-transform-unicode-sets-regex@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.22.5.tgz#77788060e511b708ffc7d42fdfbc5b37c3004e91" - integrity sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.22.5" - "@babel/helper-plugin-utils" "^7.22.5" - -"@babel/preset-env@^7.11.0": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.10.tgz#3263b9fe2c8823d191d28e61eac60a79f9ce8a0f" - integrity sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A== - dependencies: - "@babel/compat-data" "^7.22.9" - "@babel/helper-compilation-targets" "^7.22.10" - "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-validator-option" "^7.22.5" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.22.5" - "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-class-properties" "^7.12.13" - "@babel/plugin-syntax-class-static-block" "^7.14.5" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-import-assertions" "^7.22.5" - "@babel/plugin-syntax-import-attributes" "^7.22.5" - "@babel/plugin-syntax-import-meta" "^7.10.4" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-private-property-in-object" "^7.14.5" - "@babel/plugin-syntax-top-level-await" "^7.14.5" - "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.22.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.10" - "@babel/plugin-transform-async-to-generator" "^7.22.5" - "@babel/plugin-transform-block-scoped-functions" "^7.22.5" - "@babel/plugin-transform-block-scoping" "^7.22.10" - "@babel/plugin-transform-class-properties" "^7.22.5" - "@babel/plugin-transform-class-static-block" "^7.22.5" - "@babel/plugin-transform-classes" "^7.22.6" - "@babel/plugin-transform-computed-properties" "^7.22.5" - "@babel/plugin-transform-destructuring" "^7.22.10" - "@babel/plugin-transform-dotall-regex" "^7.22.5" - "@babel/plugin-transform-duplicate-keys" "^7.22.5" - "@babel/plugin-transform-dynamic-import" "^7.22.5" - "@babel/plugin-transform-exponentiation-operator" "^7.22.5" - "@babel/plugin-transform-export-namespace-from" "^7.22.5" - "@babel/plugin-transform-for-of" "^7.22.5" - "@babel/plugin-transform-function-name" "^7.22.5" - "@babel/plugin-transform-json-strings" "^7.22.5" - "@babel/plugin-transform-literals" "^7.22.5" - "@babel/plugin-transform-logical-assignment-operators" "^7.22.5" - "@babel/plugin-transform-member-expression-literals" "^7.22.5" - "@babel/plugin-transform-modules-amd" "^7.22.5" - "@babel/plugin-transform-modules-commonjs" "^7.22.5" - "@babel/plugin-transform-modules-systemjs" "^7.22.5" - "@babel/plugin-transform-modules-umd" "^7.22.5" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.22.5" - "@babel/plugin-transform-new-target" "^7.22.5" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.22.5" - "@babel/plugin-transform-numeric-separator" "^7.22.5" - "@babel/plugin-transform-object-rest-spread" "^7.22.5" - "@babel/plugin-transform-object-super" "^7.22.5" - "@babel/plugin-transform-optional-catch-binding" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.10" - "@babel/plugin-transform-parameters" "^7.22.5" - "@babel/plugin-transform-private-methods" "^7.22.5" - "@babel/plugin-transform-private-property-in-object" "^7.22.5" - "@babel/plugin-transform-property-literals" "^7.22.5" - "@babel/plugin-transform-regenerator" "^7.22.10" - "@babel/plugin-transform-reserved-words" "^7.22.5" - "@babel/plugin-transform-shorthand-properties" "^7.22.5" - "@babel/plugin-transform-spread" "^7.22.5" - "@babel/plugin-transform-sticky-regex" "^7.22.5" - "@babel/plugin-transform-template-literals" "^7.22.5" - "@babel/plugin-transform-typeof-symbol" "^7.22.5" - "@babel/plugin-transform-unicode-escapes" "^7.22.10" - "@babel/plugin-transform-unicode-property-regex" "^7.22.5" - "@babel/plugin-transform-unicode-regex" "^7.22.5" - "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" - "@babel/preset-modules" "0.1.6-no-external-plugins" - "@babel/types" "^7.22.10" - babel-plugin-polyfill-corejs2 "^0.4.5" - babel-plugin-polyfill-corejs3 "^0.8.3" - babel-plugin-polyfill-regenerator "^0.5.2" - core-js-compat "^3.31.0" - semver "^6.3.1" - -"@babel/preset-modules@0.1.6-no-external-plugins": - version "0.1.6-no-external-plugins" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" - integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/regjsgen@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" - integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== - -"@babel/runtime@^7.11.0", "@babel/runtime@^7.8.4": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.10.tgz#ae3e9631fd947cb7e3610d3e9d8fef5f76696682" - integrity sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ== - dependencies: - regenerator-runtime "^0.14.0" - -"@babel/template@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" - integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== - dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" - -"@babel/template@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.25.9.tgz#ecb62d81a8a6f5dc5fe8abfc3901fc52ddf15016" - integrity sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg== - dependencies: - "@babel/code-frame" "^7.25.9" - "@babel/parser" "^7.25.9" - "@babel/types" "^7.25.9" - -"@babel/traverse@^7.22.10", "@babel/traverse@^7.22.5": - version "7.26.7" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.26.7.tgz#99a0a136f6a75e7fb8b0a1ace421e0b25994b8bb" - integrity sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA== - dependencies: - "@babel/code-frame" "^7.26.2" - "@babel/generator" "^7.26.5" - "@babel/parser" "^7.26.7" - "@babel/template" "^7.25.9" - "@babel/types" "^7.26.7" - debug "^4.3.1" - globals "^11.1.0" - -"@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.4.4": - version "7.22.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" - integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== - dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" - to-fast-properties "^2.0.0" - -"@babel/types@^7.25.9", "@babel/types@^7.26.5", "@babel/types@^7.26.7": - version "7.26.7" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.26.7.tgz#5e2b89c0768e874d4d061961f3a5a153d71dc17a" - integrity sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg== - dependencies: - "@babel/helper-string-parser" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - -"@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" - integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/gen-mapping@^0.3.5": - version "0.3.8" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz#4f0e06362e01362f823d348f1872b08f666d8142" - integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA== - dependencies: - "@jridgewell/set-array" "^1.2.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.24" - -"@jridgewell/resolve-uri@^3.1.0": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" - integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== - -"@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/set-array@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" - integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== - -"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": - version "1.4.15" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" - integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== - -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.19" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" - integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": - version "0.3.25" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" - integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== - dependencies: - "@jridgewell/resolve-uri" "^3.1.0" - "@jridgewell/sourcemap-codec" "^1.4.14" - -"@mrmlnc/readdir-enhanced@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" - integrity sha512-bPHp6Ji8b41szTOcaP63VlnbbO5Ny6dwAATtY6JTjh5N2OLrb5Qk/Th5cRkRQhkWCt+EJsYrNB0MiL+Gpn6e3g== - dependencies: - call-me-maybe "^1.0.1" - glob-to-regexp "^0.3.0" - -"@nodelib/fs.stat@^1.1.2": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b" - integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw== - -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@types/body-parser@*": - version "1.19.2" - resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0" - integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/connect-history-api-fallback@*": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#9fd20b3974bdc2bcd4ac6567e2e0f6885cb2cf41" - integrity sha512-4x5FkPpLipqwthjPsF7ZRbOv3uoLUFkTA9G9v583qi4pACvq0uTELrB8OLUzPWUI4IJIyvM85vzkV1nyiI2Lig== - dependencies: - "@types/express-serve-static-core" "*" - "@types/node" "*" - -"@types/connect@*": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== - dependencies: - "@types/node" "*" - -"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": - version "4.17.35" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.35.tgz#c95dd4424f0d32e525d23812aa8ab8e4d3906c4f" - integrity sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/send" "*" - -"@types/express@*": - version "4.17.17" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.17.tgz#01d5437f6ef9cfa8668e616e13c2f2ac9a491ae4" - integrity sha512-Q4FmmuLGBG58btUnfS1c1r/NQdlp3DMfGDGig8WhfpA2YRUtEkxAjkZb0yvplJGYdF1fsQ81iMDcH24sSCNC/Q== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/glob@^7.1.1": - version "7.2.0" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb" - integrity sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - -"@types/highlight.js@^9.7.0": - version "9.12.4" - resolved "https://registry.yarnpkg.com/@types/highlight.js/-/highlight.js-9.12.4.tgz#8c3496bd1b50cc04aeefd691140aa571d4dbfa34" - integrity sha512-t2szdkwmg2JJyuCM20e8kR2X59WCE5Zkl4bzm1u1Oukjm79zpbiAv+QjnwLnuuV0WHEcX2NgUItu0pAMKuOPww== - -"@types/http-errors@*": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/http-errors/-/http-errors-2.0.1.tgz#20172f9578b225f6c7da63446f56d4ce108d5a65" - integrity sha512-/K3ds8TRAfBvi5vfjuz8y6+GiAYBZ0x4tXv1Av6CWBWn0IlADc+ZX9pMq7oU0fNQPnBwIZl3rmeLp6SBApbxSQ== - -"@types/http-proxy@^1.17.5": - version "1.17.11" - resolved "https://registry.yarnpkg.com/@types/http-proxy/-/http-proxy-1.17.11.tgz#0ca21949a5588d55ac2b659b69035c84bd5da293" - integrity sha512-HC8G7c1WmaF2ekqpnFq626xd3Zz0uvaqFmBJNRZCGEZCXkvSdJoNFn/8Ygbd9fKNQj8UzLdCETaI0UWPAjK7IA== - dependencies: - "@types/node" "*" - -"@types/json-schema@^7.0.5": - version "7.0.12" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" - integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== - -"@types/linkify-it@*": - version "3.0.2" - resolved "https://registry.yarnpkg.com/@types/linkify-it/-/linkify-it-3.0.2.tgz#fd2cd2edbaa7eaac7e7f3c1748b52a19143846c9" - integrity sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA== - -"@types/markdown-it@^10.0.0": - version "10.0.3" - resolved "https://registry.yarnpkg.com/@types/markdown-it/-/markdown-it-10.0.3.tgz#a9800d14b112c17f1de76ec33eff864a4815eec7" - integrity sha512-daHJk22isOUvNssVGF2zDnnSyxHhFYhtjeX4oQaKD6QzL3ZR1QSgiD1g+Q6/WSWYVogNXYDXODtbgW/WiFCtyw== - dependencies: - "@types/highlight.js" "^9.7.0" - "@types/linkify-it" "*" - "@types/mdurl" "*" - highlight.js "^9.7.0" - -"@types/mdurl@*": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@types/mdurl/-/mdurl-1.0.2.tgz#e2ce9d83a613bacf284c7be7d491945e39e1f8e9" - integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== - -"@types/mime@*": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" - integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== - -"@types/mime@^1": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" - integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== - -"@types/minimatch@*": - version "5.1.2" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-5.1.2.tgz#07508b45797cb81ec3f273011b054cd0755eddca" - integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== - -"@types/node@*": - version "20.5.0" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.5.0.tgz#7fc8636d5f1aaa3b21e6245e97d56b7f56702313" - integrity sha512-Mgq7eCtoTjT89FqNoTzzXg2XvCi5VMhRV6+I2aYanc6kQCBImeNaAYRs/DyoVqk1YEUJK5gN9VO7HRIdz4Wo3Q== - -"@types/q@^1.5.1": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.5.tgz#75a2a8e7d8ab4b230414505d92335d1dcb53a6df" - integrity sha512-L28j2FcJfSZOnL1WBjDYp2vUHCeIFlyYI/53EwD/rKUBQ7MtUUfbQWiyKJGpcnv4/WgrhWsFKrcPstcAt/J0tQ== - -"@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== - -"@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== - -"@types/send@*": - version "0.17.1" - resolved "https://registry.yarnpkg.com/@types/send/-/send-0.17.1.tgz#ed4932b8a2a805f1fe362a70f4e62d0ac994e301" - integrity sha512-Cwo8LE/0rnvX7kIIa3QHCkcuF21c05Ayb0ZfxPiv0W8VRiZiNW/WuRupHKpqqGVGf7SUA44QSOUKaEd9lIrd/Q== - dependencies: - "@types/mime" "^1" - "@types/node" "*" - -"@types/serve-static@*": - version "1.15.2" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.15.2.tgz#3e5419ecd1e40e7405d34093f10befb43f63381a" - integrity sha512-J2LqtvFYCzaj8pVYKw8klQXrLLk7TBZmQ4ShlcdkELFKGwGMfevMLneMMRkMgZxotOD9wg497LpC7O8PcvAmfw== - dependencies: - "@types/http-errors" "*" - "@types/mime" "*" - "@types/node" "*" - -"@types/source-list-map@*": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" - integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== - -"@types/tapable@^1": - version "1.0.8" - resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310" - integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ== - -"@types/uglify-js@*": - version "3.17.1" - resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.17.1.tgz#e0ffcef756476410e5bce2cb01384ed878a195b5" - integrity sha512-GkewRA4i5oXacU/n4MA9+bLgt5/L3F1mKrYvFGm7r2ouLXhRKjuWwo9XHNnbx6WF3vlGW21S3fCvgqxvxXXc5g== - dependencies: - source-map "^0.6.1" - -"@types/webpack-dev-server@^3": - version "3.11.6" - resolved "https://registry.yarnpkg.com/@types/webpack-dev-server/-/webpack-dev-server-3.11.6.tgz#d8888cfd2f0630203e13d3ed7833a4d11b8a34dc" - integrity sha512-XCph0RiiqFGetukCTC3KVnY1jwLcZ84illFRMbyFzCcWl90B/76ew0tSqF46oBhnLC4obNDG7dMO0JfTN0MgMQ== - dependencies: - "@types/connect-history-api-fallback" "*" - "@types/express" "*" - "@types/serve-static" "*" - "@types/webpack" "^4" - http-proxy-middleware "^1.0.0" - -"@types/webpack-sources@*": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b" - integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg== - dependencies: - "@types/node" "*" - "@types/source-list-map" "*" - source-map "^0.7.3" - -"@types/webpack@^4": - version "4.41.33" - resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.33.tgz#16164845a5be6a306bcbe554a8e67f9cac215ffc" - integrity sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g== - dependencies: - "@types/node" "*" - "@types/tapable" "^1" - "@types/uglify-js" "*" - "@types/webpack-sources" "*" - anymatch "^3.0.0" - source-map "^0.6.0" - -"@vue/babel-helper-vue-jsx-merge-props@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-jsx-merge-props/-/babel-helper-vue-jsx-merge-props-1.4.0.tgz#8d53a1e21347db8edbe54d339902583176de09f2" - integrity sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA== - -"@vue/babel-helper-vue-transform-on@^1.1.5": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.1.5.tgz#a976486b21e108e545524fe41ffe3fc9bbc28c7f" - integrity sha512-SgUymFpMoAyWeYWLAY+MkCK3QEROsiUnfaw5zxOVD/M64KQs8D/4oK6Q5omVA2hnvEOE0SCkH2TZxs/jnnUj7w== - -"@vue/babel-plugin-jsx@^1.0.3": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.1.5.tgz#5088bae7dbb83531d94df3742ff650c12fd54973" - integrity sha512-nKs1/Bg9U1n3qSWnsHhCVQtAzI6aQXqua8j/bZrau8ywT1ilXQbK4FwEJGmU8fV7tcpuFvWmmN7TMmV1OBma1g== - dependencies: - "@babel/helper-module-imports" "^7.22.5" - "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" - "@vue/babel-helper-vue-transform-on" "^1.1.5" - camelcase "^6.3.0" - html-tags "^3.3.1" - svg-tags "^1.0.0" - -"@vue/babel-plugin-transform-vue-jsx@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vue/babel-plugin-transform-vue-jsx/-/babel-plugin-transform-vue-jsx-1.4.0.tgz#4d4b3d46a39ea62b7467dd6e26ce47f7ceafb2fe" - integrity sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA== - dependencies: - "@babel/helper-module-imports" "^7.0.0" - "@babel/plugin-syntax-jsx" "^7.2.0" - "@vue/babel-helper-vue-jsx-merge-props" "^1.4.0" - html-tags "^2.0.0" - lodash.kebabcase "^4.1.1" - svg-tags "^1.0.0" - -"@vue/babel-preset-app@^4.1.2": - version "4.5.19" - resolved "https://registry.yarnpkg.com/@vue/babel-preset-app/-/babel-preset-app-4.5.19.tgz#baee457da0065c016f74fac4149f7c97631ba5a7" - integrity sha512-VCNRiAt2P/bLo09rYt3DLe6xXUMlhJwrvU18Ddd/lYJgC7s8+wvhgYs+MTx4OiAXdu58drGwSBO9SPx7C6J82Q== - dependencies: - "@babel/core" "^7.11.0" - "@babel/helper-compilation-targets" "^7.9.6" - "@babel/helper-module-imports" "^7.8.3" - "@babel/plugin-proposal-class-properties" "^7.8.3" - "@babel/plugin-proposal-decorators" "^7.8.3" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - "@babel/plugin-syntax-jsx" "^7.8.3" - "@babel/plugin-transform-runtime" "^7.11.0" - "@babel/preset-env" "^7.11.0" - "@babel/runtime" "^7.11.0" - "@vue/babel-plugin-jsx" "^1.0.3" - "@vue/babel-preset-jsx" "^1.2.4" - babel-plugin-dynamic-import-node "^2.3.3" - core-js "^3.6.5" - core-js-compat "^3.6.5" - semver "^6.1.0" - -"@vue/babel-preset-jsx@^1.2.4": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vue/babel-preset-jsx/-/babel-preset-jsx-1.4.0.tgz#f4914ba314235ab097bc4372ed67473c0780bfcc" - integrity sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA== - dependencies: - "@vue/babel-helper-vue-jsx-merge-props" "^1.4.0" - "@vue/babel-plugin-transform-vue-jsx" "^1.4.0" - "@vue/babel-sugar-composition-api-inject-h" "^1.4.0" - "@vue/babel-sugar-composition-api-render-instance" "^1.4.0" - "@vue/babel-sugar-functional-vue" "^1.4.0" - "@vue/babel-sugar-inject-h" "^1.4.0" - "@vue/babel-sugar-v-model" "^1.4.0" - "@vue/babel-sugar-v-on" "^1.4.0" - -"@vue/babel-sugar-composition-api-inject-h@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-composition-api-inject-h/-/babel-sugar-composition-api-inject-h-1.4.0.tgz#187e1389f8871d89ece743bb50aed713be9d6c85" - integrity sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - -"@vue/babel-sugar-composition-api-render-instance@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-composition-api-render-instance/-/babel-sugar-composition-api-render-instance-1.4.0.tgz#2c1607ae6dffdab47e785bc01fa45ba756e992c1" - integrity sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - -"@vue/babel-sugar-functional-vue@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-functional-vue/-/babel-sugar-functional-vue-1.4.0.tgz#60da31068567082287c7337c66ef4df04e0a1029" - integrity sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - -"@vue/babel-sugar-inject-h@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-inject-h/-/babel-sugar-inject-h-1.4.0.tgz#bf39aa6631fb1d0399b1c49b4c59e1c8899b4363" - integrity sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - -"@vue/babel-sugar-v-model@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-model/-/babel-sugar-v-model-1.4.0.tgz#a51d986609f430c4f70ada3a93cc560a2970f720" - integrity sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - "@vue/babel-helper-vue-jsx-merge-props" "^1.4.0" - "@vue/babel-plugin-transform-vue-jsx" "^1.4.0" - camelcase "^5.0.0" - html-tags "^2.0.0" - svg-tags "^1.0.0" - -"@vue/babel-sugar-v-on@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@vue/babel-sugar-v-on/-/babel-sugar-v-on-1.4.0.tgz#43b7106a9672d8cbeefc0eb8afe1d376edc6166e" - integrity sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA== - dependencies: - "@babel/plugin-syntax-jsx" "^7.2.0" - "@vue/babel-plugin-transform-vue-jsx" "^1.4.0" - camelcase "^5.0.0" - -"@vue/compiler-sfc@2.7.14": - version "2.7.14" - resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-2.7.14.tgz#3446fd2fbb670d709277fc3ffa88efc5e10284fd" - integrity sha512-aNmNHyLPsw+sVvlQFQ2/8sjNuLtK54TC6cuKnVzAY93ks4ZBrvwQSnkkIh7bsbNhum5hJBS00wSDipQ937f5DA== - dependencies: - "@babel/parser" "^7.18.4" - postcss "^8.4.14" - source-map "^0.6.1" - -"@vue/component-compiler-utils@^3.1.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz#f9f5fb53464b0c37b2c8d2f3fbfe44df60f61dc9" - integrity sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ== - dependencies: - consolidate "^0.15.1" - hash-sum "^1.0.2" - lru-cache "^4.1.2" - merge-source-map "^1.1.0" - postcss "^7.0.36" - postcss-selector-parser "^6.0.2" - source-map "~0.6.1" - vue-template-es2015-compiler "^1.9.0" - optionalDependencies: - prettier "^1.18.2 || ^2.0.0" - -"@vuepress/core@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/core/-/core-1.9.10.tgz#ce17d603a0195ab2b8152f5a33bea274869184a3" - integrity sha512-H9ddo5fSinPb8QYl8OJFbZikMpOW84bm/U3Drzz8CnCXNtpda7CU2wX/XzOhe98G8jp45xhtZRkxOrqzBBAShA== - dependencies: - "@babel/core" "^7.8.4" - "@vue/babel-preset-app" "^4.1.2" - "@vuepress/markdown" "1.9.10" - "@vuepress/markdown-loader" "1.9.10" - "@vuepress/plugin-last-updated" "1.9.10" - "@vuepress/plugin-register-components" "1.9.10" - "@vuepress/shared-utils" "1.9.10" - "@vuepress/types" "1.9.10" - autoprefixer "^9.5.1" - babel-loader "^8.0.4" - bundle-require "2.1.8" - cache-loader "^3.0.0" - chokidar "^2.0.3" - connect-history-api-fallback "^1.5.0" - copy-webpack-plugin "^5.0.2" - core-js "^3.6.4" - cross-spawn "^6.0.5" - css-loader "^2.1.1" - esbuild "0.14.7" - file-loader "^3.0.1" - js-yaml "^3.13.1" - lru-cache "^5.1.1" - mini-css-extract-plugin "0.6.0" - optimize-css-assets-webpack-plugin "^5.0.1" - portfinder "^1.0.13" - postcss-loader "^3.0.0" - postcss-safe-parser "^4.0.1" - toml "^3.0.0" - url-loader "^1.0.1" - vue "^2.6.10" - vue-loader "^15.7.1" - vue-router "^3.4.5" - vue-server-renderer "^2.6.10" - vue-template-compiler "^2.6.10" - vuepress-html-webpack-plugin "^3.2.0" - vuepress-plugin-container "^2.0.2" - webpack "^4.8.1" - webpack-chain "^6.0.0" - webpack-dev-server "^3.5.1" - webpack-merge "^4.1.2" - webpackbar "3.2.0" - -"@vuepress/markdown-loader@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/markdown-loader/-/markdown-loader-1.9.10.tgz#076619a9079d4955bb73b59dc9e474c488d49bb7" - integrity sha512-94BlwKc+lOaN/A5DkyA9KWHvMlMC1sWunAXE3Tv0WYzgYLDs9QqCsx7L5kLkpcOOVVm/8kBJumnXvVBwhqJddw== - dependencies: - "@vuepress/markdown" "1.9.10" - loader-utils "^1.1.0" - lru-cache "^5.1.1" - -"@vuepress/markdown@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/markdown/-/markdown-1.9.10.tgz#63df6c6137e2d6bac2d03b5911e4326484761ba7" - integrity sha512-sXTLjeZzH8SQuAL5AEH0hhsMljjNJbzWbBvzaj5yQCCdf+3sp/dJ0kwnBSnQjFPPnzPg5t3tLKGUYHyW0KiKzA== - dependencies: - "@vuepress/shared-utils" "1.9.10" - markdown-it "^8.4.1" - markdown-it-anchor "^5.0.2" - markdown-it-chain "^1.3.0" - markdown-it-emoji "^1.4.0" - markdown-it-table-of-contents "^0.4.0" - prismjs "^1.13.0" - -"@vuepress/plugin-active-header-links@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-active-header-links/-/plugin-active-header-links-1.9.10.tgz#bcd96ee7c156310de30a9565ab9ca18e72f18a5a" - integrity sha512-2dRr3DE2UBFXhyMtLR3sGTdRyDM8YStuY6AOoQmoSgwy1IHt7PO7ypOuf1akF+1Nv8Q2aISU06q6TExZouu3Mw== - dependencies: - "@vuepress/types" "1.9.10" - lodash.debounce "^4.0.8" - -"@vuepress/plugin-last-updated@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-last-updated/-/plugin-last-updated-1.9.10.tgz#e2564512fe5652be892351cd95c5e611fd11a3be" - integrity sha512-YxzWGF/OfU6WsHSynZFn74NGGp7dY27Bjy9JyyFo8wF5+2V1gpyDjveHKHGKugS/pMXlxfjzhv9E2Wmy9R7Iog== - dependencies: - "@vuepress/types" "1.9.10" - cross-spawn "^6.0.5" - -"@vuepress/plugin-nprogress@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-nprogress/-/plugin-nprogress-1.9.10.tgz#945142c2db4a2a11942c063d5d0f01bc5d295f49" - integrity sha512-I1kkm6yWUQd7vwiV3lEDVpVP0Lr04K0zlczU502lDUa1RufSZ7vt+mlF5fOM28GqT+pKTEToWmm+VNT/R3qvMQ== - dependencies: - "@vuepress/types" "1.9.10" - nprogress "^0.2.0" - -"@vuepress/plugin-register-components@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-register-components/-/plugin-register-components-1.9.10.tgz#3f8fd91acefec32b115d4dce89b14863cbe29212" - integrity sha512-sgdJ5OydTPZAoTkselpvVP3Xsd6bfZ0FpaxOTinal0gJ99h49lvLu9bvzMx13rdGRFO/kRXn0qQQpwKTAfTPqA== - dependencies: - "@vuepress/shared-utils" "1.9.10" - "@vuepress/types" "1.9.10" - -"@vuepress/plugin-search@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/plugin-search/-/plugin-search-1.9.10.tgz#313a504020d60a2f694b27a8b6457ee99b4c71e7" - integrity sha512-bn2XJikaRgQZXvu8upCjOWrxbLHIRTqnJ3w7G0mo6jCYWGVsHNo6XhVpqylpLR2PWnHT/ImO2bGo38/5Bag/tQ== - dependencies: - "@vuepress/types" "1.9.10" - -"@vuepress/shared-utils@1.9.10", "@vuepress/shared-utils@^1.2.0": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/shared-utils/-/shared-utils-1.9.10.tgz#45aae7fcf07a2067fb340c71e5bd96837b48dfee" - integrity sha512-M9A3DocPih+V8dKK2Zg9FJQ/f3JZrYsdaM/vQ9F48l8bPlzxw5NvqXIYMK4kKcGEyerQNTWCudoCpLL5uiU0hg== - dependencies: - chalk "^2.3.2" - escape-html "^1.0.3" - fs-extra "^7.0.1" - globby "^9.2.0" - gray-matter "^4.0.1" - hash-sum "^1.0.2" - semver "^6.0.0" - toml "^3.0.0" - upath "^1.1.0" - -"@vuepress/theme-default@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/theme-default/-/theme-default-1.9.10.tgz#6e916e2164ab3d9cc9567610e4ada9c3a568f206" - integrity sha512-XnXn9t+pYCIhWi3cZXJlighuy93FFm5yXdISAAlFlcNkshuGtqamkjacHV8q/QZMfOhSIs6wX7Hj88u2IsT5mw== - dependencies: - "@vuepress/plugin-active-header-links" "1.9.10" - "@vuepress/plugin-nprogress" "1.9.10" - "@vuepress/plugin-search" "1.9.10" - "@vuepress/types" "1.9.10" - docsearch.js "^2.5.2" - lodash "^4.17.15" - stylus "^0.54.8" - stylus-loader "^3.0.2" - vuepress-plugin-container "^2.0.2" - vuepress-plugin-smooth-scroll "^0.0.3" - -"@vuepress/types@1.9.10": - version "1.9.10" - resolved "https://registry.yarnpkg.com/@vuepress/types/-/types-1.9.10.tgz#5b8d112a0d7dbaffce1e5d39b66754412c83ecc6" - integrity sha512-TDNQn4og85onmBpLTTXXmncW3rUnYGr2MkuI8OIFJZetDNM49t1WbjNVlrT+kx7C6qXi6okDQgrHGYXajHZWfg== - dependencies: - "@types/markdown-it" "^10.0.0" - "@types/webpack-dev-server" "^3" - webpack-chain "^6.0.0" - -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== - -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== - -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== - -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== - -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== - -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: - version "1.3.8" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" - integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== - dependencies: - mime-types "~2.1.34" - negotiator "0.6.3" - -acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - -agentkeepalive@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-2.2.0.tgz#c5d1bd4b129008f1163f236f86e5faea2026e2ef" - integrity sha512-TnB6ziK363p7lR8QpeLC8aMr8EGYBKZTpgzQLfqTs3bR0Oo5VbKdwKf8h0dSzsYrB7lSCgfJnMZKqShvlq5Oyg== - -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -algoliasearch@^3.24.5: - version "3.35.1" - resolved "https://registry.yarnpkg.com/algoliasearch/-/algoliasearch-3.35.1.tgz#297d15f534a3507cab2f5dfb996019cac7568f0c" - integrity sha512-K4yKVhaHkXfJ/xcUnil04xiSrB8B8yHZoFEhWNpXg23eiCnqvTZw1tn/SqvdsANlYHLJlKl0qi3I/Q2Sqo7LwQ== - dependencies: - agentkeepalive "^2.2.0" - debug "^2.6.9" - envify "^4.0.0" - es6-promise "^4.1.0" - events "^1.1.0" - foreach "^2.0.5" - global "^4.3.2" - inherits "^2.0.1" - isarray "^2.0.1" - load-script "^1.0.0" - object-keys "^1.0.11" - querystring-es3 "^0.2.1" - reduce "^1.0.1" - semver "^5.1.0" - tunnel-agent "^0.6.0" - -alphanum-sort@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - integrity sha512-0FcBfdcmaumGPQ0qPn7Q5qTgz/ooXgIyp1rf8ik5bGX8mpE2YHjC0P/eyQvxu1GURYQgq9ozf2mteQ5ZD9YiyQ== - -ansi-align@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" - integrity sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w== - dependencies: - string-width "^4.1.0" - -ansi-colors@^3.0.0: - version "3.2.4" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" - integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== - -ansi-escapes@^4.1.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-html-community@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" - integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== - -ansi-regex@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.1.tgz#164daac87ab2d6f6db3a29875e2d1766582dabed" - integrity sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@^3.0.0, anymatch@~3.1.2: - version "3.1.3" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" - integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA== - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q== - -array-buffer-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz#fabe8bc193fea865f317fe7807085ee0dee5aead" - integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== - dependencies: - call-bind "^1.0.2" - is-array-buffer "^3.0.1" - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - -array-flatten@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - -array-union@^1.0.1, array-union@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng== - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q== - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ== - -array.prototype.reduce@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" - integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - es-array-method-boxes-properly "^1.0.0" - is-string "^1.0.7" - -arraybuffer.prototype.slice@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.1.tgz#9b5ea3868a6eebc30273da577eb888381c0044bb" - integrity sha512-09x0ZWFEjj4WD8PDbykUwo3t9arLn8NIzmmYEJFpYekOAQjpkGSyrQhNoRTcwwcFRu+ycWF78QZ63oWTqSjBcw== - dependencies: - array-buffer-byte-length "^1.0.0" - call-bind "^1.0.2" - define-properties "^1.2.0" - get-intrinsic "^1.2.1" - is-array-buffer "^3.0.2" - is-shared-array-buffer "^1.0.2" - -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - -asn1@~0.2.3: - version "0.2.6" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" - integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw== - -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw== - -async-each@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.6.tgz#52f1d9403818c179b7561e11a5d1b77eb2160e77" - integrity sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg== - -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - -async@^2.6.4: - version "2.6.4" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221" - integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== - dependencies: - lodash "^4.17.14" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -autocomplete.js@0.36.0: - version "0.36.0" - resolved "https://registry.yarnpkg.com/autocomplete.js/-/autocomplete.js-0.36.0.tgz#94fe775fe64b6cd42e622d076dc7fd26bedd837b" - integrity sha512-jEwUXnVMeCHHutUt10i/8ZiRaCb0Wo+ZyKxeGsYwBDtw6EJHqEeDrq4UwZRD8YBSvp3g6klP678il2eeiVXN2Q== - dependencies: - immediate "^3.2.3" - -autoprefixer@^9.5.1: - version "9.8.8" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.8.tgz#fd4bd4595385fa6f06599de749a4d5f7a474957a" - integrity sha512-eM9d/swFopRt5gdJ7jrpCwgvEMIayITpojhkkSMRsFHYuH5bkSQ4p/9qTEHtmNudUZh22Tehu7I6CxAW0IXTKA== - dependencies: - browserslist "^4.12.0" - caniuse-lite "^1.0.30001109" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - picocolors "^0.2.1" - postcss "^7.0.32" - postcss-value-parser "^4.1.0" - -available-typed-arrays@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" - integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA== - -aws4@^1.8.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" - integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== - -babel-loader@^8.0.4: - version "8.3.0" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.3.0.tgz#124936e841ba4fe8176786d6ff28add1f134d6a8" - integrity sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q== - dependencies: - find-cache-dir "^3.3.1" - loader-utils "^2.0.0" - make-dir "^3.1.0" - schema-utils "^2.6.5" - -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - -babel-plugin-polyfill-corejs2@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz#8097b4cb4af5b64a1d11332b6fb72ef5e64a054c" - integrity sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg== - dependencies: - "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.4.2" - semver "^6.3.1" - -babel-plugin-polyfill-corejs3@^0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz#b4f719d0ad9bb8e0c23e3e630c0c8ec6dd7a1c52" - integrity sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.2" - core-js-compat "^3.31.0" - -babel-plugin-polyfill-regenerator@^0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz#80d0f3e1098c080c8b5a65f41e9427af692dc326" - integrity sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.4.2" - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base64-js@^1.0.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== - dependencies: - tweetnacl "^0.14.3" - -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" - integrity sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q== - -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - -bluebird@^3.1.1, bluebird@^3.5.5: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70" - integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ== - -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - integrity sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg== - dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" - dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" - -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== - -boxen@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz#e411b62357d6d6d36587c8ac3d5d974daa070e64" - integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^5.3.1" - chalk "^3.0.0" - cli-boxes "^2.2.0" - string-width "^4.1.0" - term-size "^2.1.0" - type-fest "^0.8.1" - widest-line "^3.1.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brorand@^1.0.1, brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w== - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.21.9: - version "4.21.10" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" - integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== - dependencies: - caniuse-lite "^1.0.30001517" - electron-to-chromium "^1.4.477" - node-releases "^2.0.13" - update-browserslist-db "^1.0.11" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== - -buffer-json@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/buffer-json/-/buffer-json-2.0.0.tgz#f73e13b1e42f196fe2fd67d001c7d7107edd7c23" - integrity sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw== - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ== - -buffer@^4.3.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ== - -bundle-require@2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-2.1.8.tgz#28f6de9d4468a6b7b76fb5c9bf52e70f5091245d" - integrity sha512-oOEg3A0hy/YzvNWNowtKD0pmhZKseOFweCbgyMqTIih4gRY1nJWsvrOCT27L9NbIyL5jMjTFrAUpGxxpW68Puw== - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - -bytes@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" - integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== - -cac@^6.5.6: - version "6.7.14" - resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959" - integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ== - -cacache@^12.0.2, cacache@^12.0.3: - version "12.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -cache-loader@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/cache-loader/-/cache-loader-3.0.1.tgz#cee6cf4b3cdc7c610905b26bad6c2fc439c821af" - integrity sha512-HzJIvGiGqYsFUrMjAJNDbVZoG7qQA+vy9AIoKs7s9DscNfki0I589mf2w6/tW+kkFH3zyiknoWV5Jdynu6b/zw== - dependencies: - buffer-json "^2.0.0" - find-cache-dir "^2.1.0" - loader-utils "^1.2.3" - mkdirp "^0.5.1" - neo-async "^2.6.1" - schema-utils "^1.0.0" - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -call-me-maybe@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.2.tgz#03f964f19522ba643b1b0693acb9152fe2074baa" - integrity sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ== - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha512-JuG3qI4QOftFsZyOn1qq87fq5grLIyk1JYd5lJmdA+fG7aQ9pA/i3JIJGcO3q0MrRcHlOt1U+ZeHW8Dq9axALQ== - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha512-MCL3sf6nCSXOwCTzvPKhN18TU7AHTvdtam8DAogxcrJ8Rjfbbg7Lgng64H9Iy+vUV6VGFClN/TyxBkAebLRR4A== - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha512-ksWePWBloaWPxJYQ8TL0JHvtci6G5QTKwQ95RcWAa/lzoAKuAOflGdAK92hpHXjkwb8zLxoLNUoNYZgVsaJzvQ== - -camel-case@3.0.x: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" - integrity sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w== - dependencies: - no-case "^2.2.0" - upper-case "^1.1.1" - -camelcase@^5.0.0, camelcase@^5.2.0, camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001517: - version "1.0.30001520" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001520.tgz#62e2b7a1c7b35269594cf296a80bdf8cb9565006" - integrity sha512-tahF5O9EiiTzwTUqAeFjIZbn4Dnqxzz7ktrgGlMYNLH43Ul26IgTMH/zvL3DG0lZxBYnlT04axvInszUsZULdA== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== - -chalk@^2.3.2, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chokidar@^2.0.3, chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chokidar@^3.4.1: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -chownr@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chrome-trace-event@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" - integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== - -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== - -ci-info@^3.1.1: - version "3.8.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" - integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -clean-css@4.2.x: - version "4.2.4" - resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" - integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== - dependencies: - source-map "~0.6.0" - -cli-boxes@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz#ddd5035d25094fce220e9cab40a45840a440318f" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== - -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" - -clone-response@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3" - integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA== - dependencies: - mimic-response "^1.0.0" - -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw== - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0, color-convert@^1.9.3: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@^1.0.0, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.6.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" - integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== - dependencies: - color-convert "^1.9.3" - color-string "^1.6.0" - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@2.17.x: - version "2.17.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" - integrity sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg== - -commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commander@~2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" - integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== - -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -concat-stream@^1.5.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== - dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" - -connect-history-api-fallback@^1.5.0, connect-history-api-fallback@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" - integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== - -consola@^2.6.0: - version "2.15.3" - resolved "https://registry.yarnpkg.com/consola/-/consola-2.15.3.tgz#2e11f98d6a4be71ff72e0bdf07bd23e12cb61550" - integrity sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw== - -console-browserify@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" - integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== - -consolidate@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/consolidate/-/consolidate-0.15.1.tgz#21ab043235c71a07d45d9aad98593b0dba56bab7" - integrity sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw== - dependencies: - bluebird "^3.1.1" - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ== - -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" - integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== - -convert-source-map@^1.7.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== - -copy-webpack-plugin@^5.0.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-5.1.2.tgz#8a889e1dcafa6c91c6cd4be1ad158f1d3823bae2" - integrity sha512-Uh7crJAco3AjBvgAy9Z75CjK8IG+gxaErro71THQ+vv/bl4HaQcpkexAY8KVW/T6D2W2IRr+couF/knIRkZMIQ== - dependencies: - cacache "^12.0.3" - find-cache-dir "^2.1.0" - glob-parent "^3.1.0" - globby "^7.1.1" - is-glob "^4.0.1" - loader-utils "^1.2.3" - minimatch "^3.0.4" - normalize-path "^3.0.0" - p-limit "^2.2.1" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - webpack-log "^2.0.0" - -core-js-compat@^3.31.0, core-js-compat@^3.6.5: - version "3.32.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.0.tgz#f41574b6893ab15ddb0ac1693681bd56c8550a90" - integrity sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw== - dependencies: - browserslist "^4.21.9" - -core-js@^3.6.4, core-js@^3.6.5: - version "3.32.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.0.tgz#7643d353d899747ab1f8b03d2803b0312a0fb3b6" - integrity sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww== - -core-util-is@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ== - -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" - -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -cross-spawn@^6.0.0, cross-spawn@^6.0.5: - version "6.0.6" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57" - integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - -css-color-names@0.0.4, css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - integrity sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q== - -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== - dependencies: - postcss "^7.0.1" - timsort "^0.3.0" - -css-loader@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-2.1.1.tgz#d8254f72e412bb2238bb44dd674ffbef497333ea" - integrity sha512-OcKJU/lt232vl1P9EEDamhoO9iKY3tIjY5GU+XDLblAykTdgs6Ux9P1hTHve8nFKy5KPpOXOsVI/hIwi3841+w== - dependencies: - camelcase "^5.2.0" - icss-utils "^4.1.0" - loader-utils "^1.2.3" - normalize-path "^3.0.0" - postcss "^7.0.14" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^2.0.6" - postcss-modules-scope "^2.1.0" - postcss-modules-values "^2.0.0" - postcss-value-parser "^3.3.0" - schema-utils "^1.0.0" - -css-parse@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4" - integrity sha512-UNIFik2RgSbiTwIW1IsFwXWn6vs+bYdq83LKTSOsx7NJR7WII9dxewkHLltfTLVppoUApHV0118a4RZRI9FLwA== - dependencies: - css "^2.0.0" - -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" - integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== - dependencies: - boolbase "^1.0.0" - css-what "^3.2.1" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-select@^4.1.3: - version "4.3.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" - integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== - dependencies: - boolbase "^1.0.0" - css-what "^6.0.1" - domhandler "^4.3.1" - domutils "^2.8.0" - nth-check "^2.0.1" - -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - dependencies: - mdn-data "2.0.4" - source-map "^0.6.1" - -css-tree@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - -css-what@^3.2.1: - version "3.4.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" - integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== - -css-what@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" - integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== - -css@^2.0.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" - integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== - dependencies: - inherits "^2.0.3" - source-map "^0.6.1" - source-map-resolve "^0.5.2" - urix "^0.1.0" - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -cssnano-preset-default@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.8.tgz#920622b1fc1e95a34e8838203f1397a504f2d3ff" - integrity sha512-LdAyHuq+VRyeVREFmuxUZR1TXjQm8QQU/ktoo/x7bz+SdOge1YKc5eMN6pRW7YWBmyq59CqYba1dJ5cUukEjLQ== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.3" - postcss-unique-selectors "^4.0.1" - -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" - integrity sha512-6RIcwmV3/cBMG8Aj5gucQRsJb4vv4I4rn6YjPbVWd5+Pn/fuG+YseGvXGk00XLkoZkaj31QOD7vMUpNPC4FIuw== - -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" - integrity sha512-JPMZ1TSMRUPVIqEalIBNoBtAYbi8okvcFns4O0YIhcdGebeYZK7dMyHJiQ6GqNBA9kE0Hym4Aqym5rPdsV/4Cw== - -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== - dependencies: - postcss "^7.0.0" - -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== - -cssnano@^4.1.10: - version "4.1.11" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.11.tgz#c7b5f5b81da269cb1fd982cb960c1200910c9a99" - integrity sha512-6gZm2htn7xIPJOHY824ERgj8cNPgPxyCSnkXc4v7YvNW+TdVfzgngHcEhy/8D11kUWRUMbke+tC+AUcUsnMz2g== - dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.8" - is-resolvable "^1.0.0" - postcss "^7.0.0" - -csso@^4.0.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== - dependencies: - css-tree "^1.1.2" - -csstype@^3.1.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.2.tgz#1d4bf9d572f11c14031f0436e1c10bc1f571f50b" - integrity sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ== - -cyclist@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.2.tgz#673b5f233bf34d8e602b949429f8171d9121bea3" - integrity sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA== - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g== - dependencies: - assert-plus "^1.0.0" - -de-indent@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" - integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.0, debug@^4.1.1: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@^4.3.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" - integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== - dependencies: - ms "^2.1.3" - -debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== - -decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== - dependencies: - mimic-response "^1.0.0" - -deep-equal@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== - dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -deepmerge@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-1.5.2.tgz#10499d868844cdad4fee0842df8c7f6f0c95a753" - integrity sha512-95k0GDqvBjZavkuvzx/YqVLv/6YYa17fz6ILMSf7neqQITCPbnfEnQvEgMPNjH4kgobe7+WIL0yJEHku+H3qtQ== - -default-gateway@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" - integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== - dependencies: - execa "^1.0.0" - ip-regex "^2.1.0" - -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -define-properties@^1.1.2, define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" - integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA== - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA== - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -del@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" - integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== - dependencies: - "@types/glob" "^7.1.1" - globby "^6.1.0" - is-path-cwd "^2.0.0" - is-path-in-cwd "^2.0.0" - p-map "^2.0.0" - pify "^4.0.1" - rimraf "^2.6.3" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -depd@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - -des.js@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.1.0.tgz#1d37f5766f3bbff4ee9638e871a8768c173b81da" - integrity sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" - integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== - -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -dir-glob@^2.0.0, dir-glob@^2.2.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.2.2.tgz#fa09f0694153c8918b18ba0deafae94769fc50c4" - integrity sha512-f9LBi5QWzIW3I6e//uxZoLBlUt9kcp66qo0sSCxL6YZKc75R1c4MFCoe/LaZiBGmgujvQdxc5Bn3QhfyvK5Hsw== - dependencies: - path-type "^3.0.0" - -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg== - -dns-packet@^1.3.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.4.tgz#e3455065824a2507ba886c55a89963bb107dec6f" - integrity sha512-BQ6F4vycLXBvdrJZ6S3gZewt6rcrks9KBgM9vrhW+knGRqc8uEdT7fuCwloc7nny5xNoMJ17HGH0R/6fpo8ECA== - dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" - -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - integrity sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ== - dependencies: - buffer-indexof "^1.0.0" - -docsearch.js@^2.5.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/docsearch.js/-/docsearch.js-2.6.3.tgz#57cb4600d3b6553c677e7cbbe6a734593e38625d" - integrity sha512-GN+MBozuyz664ycpZY0ecdQE0ND/LSgJKhTLA0/v3arIS3S1Rpf2OJz6A35ReMsm91V5apcmzr5/kM84cvUg+A== - dependencies: - algoliasearch "^3.24.5" - autocomplete.js "0.36.0" - hogan.js "^3.0.2" - request "^2.87.0" - stack-utils "^1.0.1" - to-factory "^1.0.0" - zepto "^1.2.0" - -dom-converter@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" - integrity sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA== - dependencies: - utila "~0.4" - -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -dom-serializer@^1.0.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" - integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.2.0" - entities "^2.0.0" - -dom-walk@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" - integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== - -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - -domelementtype@1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1, domelementtype@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" - integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== - -domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" - integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== - dependencies: - domelementtype "^2.2.0" - -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^2.5.2, domutils@^2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" - integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== - dependencies: - dom-serializer "^1.0.1" - domelementtype "^2.2.0" - domhandler "^4.2.0" - -dot-prop@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" - integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== - dependencies: - is-obj "^2.0.0" - -duplexer3@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.5.tgz#0b5e4d7bad5de8901ea4440624c8e1d20099217e" - integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== - -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw== - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== - -electron-to-chromium@^1.4.477: - version "1.4.490" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.490.tgz#d99286f6e915667fa18ea4554def1aa60eb4d5f1" - integrity sha512-6s7NVJz+sATdYnIwhdshx/N/9O6rvMxmhVoDSDFdj6iA45gHR8EQje70+RYsF4GeB+k0IeNSBnP7yG9ZXJFr7A== - -elliptic@^6.5.3: - version "6.6.1" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.6.1.tgz#3b8ffb02670bf69e382c7f65bf524c97c5405c06" - integrity sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha512-knHEZMgs8BB+MInokmNTg/OyPlAddghe1YBgNwJBc5zsJi/uyIcXoSDsL/W9ymOsBoBGdPIHXYJ9+qKFwRwDng== - -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== - -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -enhanced-resolve@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" - integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" - -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - -entities@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== - -envify@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/envify/-/envify-4.1.0.tgz#f39ad3db9d6801b4e6b478b61028d3f0b6819f7e" - integrity sha512-IKRVVoAYr4pIx4yIWNsz9mOsboxlNXiu7TNBnem/K/uTHdkyzXWDzHCK7UTolqBbgaBz0tQHsD3YNls0uIIjiw== - dependencies: - esprima "^4.0.0" - through "~2.3.4" - -envinfo@^7.2.0: - version "7.10.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" - integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw== - -errno@^0.1.3, errno@~0.1.7: - version "0.1.8" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" - integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== - dependencies: - prr "~1.0.1" - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: - version "1.22.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" - integrity sha512-ioRRcXMO6OFyRpyzV3kE1IIBd4WG5/kltnzdxSCqoP8CMGs/Li+M1uF5o7lOkZVFjDs+NLesthnF66Pg/0q0Lw== - dependencies: - array-buffer-byte-length "^1.0.0" - arraybuffer.prototype.slice "^1.0.1" - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - es-set-tostringtag "^2.0.1" - es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.2.1" - get-symbol-description "^1.0.0" - globalthis "^1.0.3" - gopd "^1.0.1" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-proto "^1.0.1" - has-symbols "^1.0.3" - internal-slot "^1.0.5" - is-array-buffer "^3.0.2" - is-callable "^1.2.7" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-typed-array "^1.1.10" - is-weakref "^1.0.2" - object-inspect "^1.12.3" - object-keys "^1.1.1" - object.assign "^4.1.4" - regexp.prototype.flags "^1.5.0" - safe-array-concat "^1.0.0" - safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" - typed-array-buffer "^1.0.0" - typed-array-byte-length "^1.0.0" - typed-array-byte-offset "^1.0.0" - typed-array-length "^1.0.4" - unbox-primitive "^1.0.2" - which-typed-array "^1.1.10" - -es-array-method-boxes-properly@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" - integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== - -es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== - dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" - has-tostringtag "^1.0.0" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es6-promise@^4.1.0: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -esbuild-android-arm64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.7.tgz#8c78cbb617f9f216abfb5a84cca453b51421a1b6" - integrity sha512-9/Q1NC4JErvsXzJKti0NHt+vzKjZOgPIjX/e6kkuCzgfT/GcO3FVBcGIv4HeJG7oMznE6KyKhvLrFgt7CdU2/w== - -esbuild-darwin-64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.7.tgz#7424bdb64c104556d36b7429af79ab51415ab8f4" - integrity sha512-Z9X+3TT/Xj+JiZTVlwHj2P+8GoiSmUnGVz0YZTSt8WTbW3UKw5Pw2ucuJ8VzbD2FPy0jbIKJkko/6CMTQchShQ== - -esbuild-darwin-arm64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.7.tgz#6a243dc0132aeb11c1991f968a6a9e393f43c6bc" - integrity sha512-68e7COhmwIiLXBEyxUxZSSU0akgv8t3e50e2QOtKdBUE0F6KIRISzFntLe2rYlNqSsjGWsIO6CCc9tQxijjSkw== - -esbuild-freebsd-64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.7.tgz#e7281e50522e724c4da502504dcd75be0db46c94" - integrity sha512-76zy5jAjPiXX/S3UvRgG85Bb0wy0zv/J2lel3KtHi4V7GUTBfhNUPt0E5bpSXJ6yMT7iThhnA5rOn+IJiUcslQ== - -esbuild-freebsd-arm64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.7.tgz#31e513098efd181d76a3ba3ea285836d37f018a3" - integrity sha512-lSlYNLiqyzd7qCN5CEOmLxn7MhnGHPcu5KuUYOG1i+t5A6q7LgBmfYC9ZHJBoYyow3u4CNu79AWHbvVLpE/VQQ== - -esbuild-linux-32@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.7.tgz#82cf96accbf55d3007c3338dc3b3144efa9ae108" - integrity sha512-Vk28u409wVOXqTaT6ek0TnfQG4Ty1aWWfiysIaIRERkNLhzLhUf4i+qJBN8mMuGTYOkE40F0Wkbp6m+IidOp2A== - -esbuild-linux-64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.7.tgz#67bdfe23a6ca918a0bb8e9558a3ee0fdf98c0bc0" - integrity sha512-+Lvz6x+8OkRk3K2RtZwO+0a92jy9si9cUea5Zoru4yJ/6EQm9ENX5seZE0X9DTwk1dxJbjmLsJsd3IoowyzgVg== - -esbuild-linux-arm64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.7.tgz#f79c69ff0c176559c418de8e59aa3cf388fff992" - integrity sha512-kJd5beWSqteSAW086qzCEsH6uwpi7QRIpzYWHzEYwKKu9DiG1TwIBegQJmLpPsLp4v5RAFjea0JAmAtpGtRpqg== - -esbuild-linux-arm@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.7.tgz#3d665b35e1c27dbe1c9deb8bf956d7d1f191a21b" - integrity sha512-OzpXEBogbYdcBqE4uKynuSn5YSetCvK03Qv1HcOY1VN6HmReuatjJ21dCH+YPHSpMEF0afVCnNfffvsGEkxGJQ== - -esbuild-linux-mips64le@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.7.tgz#226114a0cc6649ba0ffd3428118a8f622872f16d" - integrity sha512-mFWpnDhZJmj/h7pxqn1GGDsKwRfqtV7fx6kTF5pr4PfXe8pIaTERpwcKkoCwZUkWAOmUEjMIUAvFM72A6hMZnA== - -esbuild-linux-ppc64le@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.7.tgz#5c67ae56517f2644d567b2ca5ecb97f9520cfc49" - integrity sha512-wM7f4M0bsQXfDL4JbbYD0wsr8cC8KaQ3RPWc/fV27KdErPW7YsqshZZSjDV0kbhzwpNNdhLItfbaRT8OE8OaKA== - -esbuild-netbsd-64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.7.tgz#69dc0469ea089013956d8c6aa71c9e7fc25fc567" - integrity sha512-J/afS7woKyzGgAL5FlgvMyqgt5wQ597lgsT+xc2yJ9/7BIyezeXutXqfh05vszy2k3kSvhLesugsxIA71WsqBw== - -esbuild-openbsd-64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.7.tgz#3a9d04ecf820708e2e5b7d26fa7332e3f19f6b6c" - integrity sha512-7CcxgdlCD+zAPyveKoznbgr3i0Wnh0L8BDGRCjE/5UGkm5P/NQko51tuIDaYof8zbmXjjl0OIt9lSo4W7I8mrw== - -esbuild-sunos-64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.7.tgz#7c33a682f0fd9565cae7df165d0e8736b7b62623" - integrity sha512-GKCafP2j/KUljVC3nesw1wLFSZktb2FGCmoT1+730zIF5O6hNroo0bSEofm6ZK5mNPnLiSaiLyRB9YFgtkd5Xg== - -esbuild-windows-32@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.7.tgz#24ec706a5f25b4499048f56146bcff0ed3839dce" - integrity sha512-5I1GeL/gZoUUdTPA0ws54bpYdtyeA2t6MNISalsHpY269zK8Jia/AXB3ta/KcDHv2SvNwabpImeIPXC/k0YW6A== - -esbuild-windows-64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.7.tgz#dd6d5b5bace93cd7a9174d31fbd727ba21885abf" - integrity sha512-CIGKCFpQOSlYsLMbxt8JjxxvVw9MlF1Rz2ABLVfFyHUF5OeqHD5fPhGrCVNaVrhO8Xrm+yFmtjcZudUGr5/WYQ== - -esbuild-windows-arm64@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.7.tgz#ecfd9ac289606f26760c4f737caaeeadfff3cfe3" - integrity sha512-eOs1eSivOqN7cFiRIukEruWhaCf75V0N8P0zP7dh44LIhLl8y6/z++vv9qQVbkBm5/D7M7LfCfCTmt1f1wHOCw== - -esbuild@0.14.7: - version "0.14.7" - resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.7.tgz#e85cead55b0e1001abf1b2ce4a11c1d4d709d13c" - integrity sha512-+u/msd6iu+HvfysUPkZ9VHm83LImmSNnecYPfFI01pQ7TTcsFR+V0BkybZX7mPtIaI7LCrse6YRj+v3eraJSgw== - optionalDependencies: - esbuild-android-arm64 "0.14.7" - esbuild-darwin-64 "0.14.7" - esbuild-darwin-arm64 "0.14.7" - esbuild-freebsd-64 "0.14.7" - esbuild-freebsd-arm64 "0.14.7" - esbuild-linux-32 "0.14.7" - esbuild-linux-64 "0.14.7" - esbuild-linux-arm "0.14.7" - esbuild-linux-arm64 "0.14.7" - esbuild-linux-mips64le "0.14.7" - esbuild-linux-ppc64le "0.14.7" - esbuild-netbsd-64 "0.14.7" - esbuild-openbsd-64 "0.14.7" - esbuild-sunos-64 "0.14.7" - esbuild-windows-32 "0.14.7" - esbuild-windows-64 "0.14.7" - esbuild-windows-arm64 "0.14.7" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz#1b2dc77003676c457ec760b2dc68edb648188675" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== - -escape-html@^1.0.3, escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esrecurse@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -events@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== - -events@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -eventsource@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-2.0.2.tgz#76dfcc02930fb2ff339520b6d290da573a9e8508" - integrity sha512-IzUmBGPR3+oUG9dUeXynyNmf91/3zUSJg1lCktzKw47OXuhco54U3r9B7O4XX+Rb1Itm9OZ2b0RkTs10bICOxA== - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA== - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -express@^4.17.1: - version "4.18.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.1" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.11.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q== - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g== - -extsprintf@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" - integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-glob@^2.2.6: - version "2.2.7" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d" - integrity sha512-g1KuQwHOZAmOZMuBtHdxDtju+T2RT8jgCC9aANsbpdiDDTSnjgfuVsIBNKbUeJI3oKMRExcfNDtJl4OhbffMsw== - dependencies: - "@mrmlnc/readdir-enhanced" "^2.2.1" - "@nodelib/fs.stat" "^1.1.2" - glob-parent "^3.1.0" - is-glob "^4.0.0" - merge2 "^1.2.3" - micromatch "^3.1.10" - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -faye-websocket@^0.11.3, faye-websocket@^0.11.4: - version "0.11.4" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - -figgy-pudding@^3.5.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" - integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== - -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-loader@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-3.0.1.tgz#f8e0ba0b599918b51adfe45d66d1e771ad560faa" - integrity sha512-4sNIOXgtH/9WZq4NvlfU3Opn5ynUsqBwSLyM+I7UOwdGigTBYfVVQEwe/msZNX/j4pCJTIM14Fsw66Svo1oVrw== - dependencies: - loader-utils "^1.0.2" - schema-utils "^1.0.0" - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ== - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - -find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-cache-dir@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" - integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - -follow-redirects@^1.0.0: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== - -for-each@^0.3.3: - version "0.3.3" - resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" - integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== - dependencies: - is-callable "^1.1.3" - -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== - -foreach@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.6.tgz#87bcc8a1a0e74000ff2bf9802110708cfb02eb6e" - integrity sha512-k6GAGDyqLe9JaebCsFCoudPPWfihKu8pylYXRlqP1J7ms39iPoTtk2fviNglIeQEwdh0bQeKJ01ZPyuyQvKzwg== - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw== - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA== - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - -from2@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - -fs-extra@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" - integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" - integrity sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA== - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - -functions-have-names@^1.2.2, functions-have-names@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.1: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-proto "^1.0.1" - has-symbols "^1.0.3" - -get-stream@^4.0.0, get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA== - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng== - dependencies: - assert-plus "^1.0.0" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA== - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-to-regexp@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" - integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== - -glob@^7.0.3, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-dirs@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz#e9046a49c806ff04d6c1825e196c8f0091e8df4d" - integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ== - dependencies: - ini "1.3.7" - -global@^4.3.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406" - integrity sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w== - dependencies: - min-document "^2.19.0" - process "^0.11.10" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globalthis@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf" - integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== - dependencies: - define-properties "^1.1.3" - -globby@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" - integrity sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw== - dependencies: - array-union "^1.0.1" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -globby@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/globby/-/globby-7.1.1.tgz#fb2ccff9401f8600945dfada97440cca972b8680" - integrity sha512-yANWAN2DUcBtuus5Cpd+SKROzXHs2iVXFZt/Ykrfz6SAXqacLX25NZpltE+39ceMexYF4TtEadjuSTw8+3wX4g== - dependencies: - array-union "^1.0.1" - dir-glob "^2.0.0" - glob "^7.1.2" - ignore "^3.3.5" - pify "^3.0.0" - slash "^1.0.0" - -globby@^9.2.0: - version "9.2.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-9.2.0.tgz#fd029a706c703d29bdd170f4b6db3a3f7a7cb63d" - integrity sha512-ollPHROa5mcxDEkwg6bPt3QbEf4pDQSNtd6JPL1YvOvAo/7/0VAm9TccUeoTmarjPw4pfUthSCqcyfNB1I3ZSg== - dependencies: - "@types/glob" "^7.1.1" - array-union "^1.0.2" - dir-glob "^2.2.2" - fast-glob "^2.2.6" - glob "^7.1.3" - ignore "^4.0.3" - pify "^4.0.1" - slash "^2.0.0" - -gopd@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" - integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== - dependencies: - get-intrinsic "^1.1.3" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.2.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" - integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== - -gray-matter@^4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" - integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== - dependencies: - js-yaml "^3.13.1" - kind-of "^6.0.2" - section-matter "^1.0.0" - strip-bom-string "^1.0.0" - -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q== - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-proto@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" - integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== - -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q== - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw== - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ== - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ== - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz#137e11354a7b5bf11aa5cb649cf0c6f3ff2b2e77" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== - -has@^1.0.0, has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash-sum@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-1.0.2.tgz#33b40777754c6432573c120cc3808bbd10d47f04" - integrity sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA== - -hash-sum@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" - integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -he@1.2.x, he@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== - -highlight.js@^9.7.0: - version "9.18.5" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825" - integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA== - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg== - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -hogan.js@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/hogan.js/-/hogan.js-3.0.2.tgz#4cd9e1abd4294146e7679e41d7898732b02c7bfd" - integrity sha512-RqGs4wavGYJWE07t35JQccByczmNUXQT0E12ZYV1VKYu5UiAU9lsos/yBAcf840+zrUQQxgVduCR5/B8nNtibg== - dependencies: - mkdirp "0.3.0" - nopt "1.0.10" - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" - integrity sha512-M5ezZw4LzXbBKMruP+BNANf0k+19hDQMgpzBIYnya//Al+fjNct9Wf3b1WedLqdEs2hKBvxq/jh+DsHJLj0F9A== - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" - integrity sha512-7Wn5GMLuHBjZCb2bTmnDOycho0p/7UVaAeqXZGbHrBCl6Yd/xDhQJAXe6Ga9AXJH2I5zY1dEdYw2u1UptnSBJA== - -html-entities@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc" - integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA== - -html-minifier@^3.2.3: - version "3.5.21" - resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-3.5.21.tgz#d0040e054730e354db008463593194015212d20c" - integrity sha512-LKUKwuJDhxNa3uf/LPR/KVjm/l3rBqtYeCOAekvG8F1vItxMUpueGd94i/asDDr8/1u7InxzFA5EeGjhhG5mMA== - dependencies: - camel-case "3.0.x" - clean-css "4.2.x" - commander "2.17.x" - he "1.2.x" - param-case "2.1.x" - relateurl "0.2.x" - uglify-js "3.4.x" - -html-tags@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" - integrity sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g== - -html-tags@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.3.1.tgz#a04026a18c882e4bba8a01a3d39cfe465d40b5ce" - integrity sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ== - -htmlparser2@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" - integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== - dependencies: - domelementtype "^2.0.1" - domhandler "^4.0.0" - domutils "^2.5.2" - entities "^2.0.0" - -http-cache-semantics@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" - integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== - -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== - -http-errors@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" - integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== - dependencies: - depd "2.0.0" - inherits "2.0.4" - setprototypeof "1.2.0" - statuses "2.0.1" - toidentifier "1.0.1" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-parser-js@>=0.5.1: - version "0.5.8" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" - integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== - -http-proxy-middleware@0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" - integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== - dependencies: - http-proxy "^1.17.0" - is-glob "^4.0.0" - lodash "^4.17.11" - micromatch "^3.1.10" - -http-proxy-middleware@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz#43700d6d9eecb7419bf086a128d0f7205d9eb665" - integrity sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg== - dependencies: - "@types/http-proxy" "^1.17.5" - http-proxy "^1.18.1" - is-glob "^4.0.1" - is-plain-obj "^3.0.0" - micromatch "^4.0.2" - -http-proxy@^1.17.0, http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ== - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== - -iconv-lite@0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -icss-replace-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" - integrity sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg== - -icss-utils@^4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" - -ieee754@^1.1.4: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" - integrity sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA== - -ignore@^3.3.5: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - -ignore@^4.0.3: - version "4.0.6" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" - integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== - -immediate@^3.2.3: - version "3.3.0" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" - integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== - -import-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" - integrity sha512-Ew5AZzJQFqrOV5BTW3EIoHAnoie1LojZLXKcCQ/yTRyVZosBhK1x1ViYjHGf5pAFOq8ZyChZp6m/fSN7pJyZtg== - dependencies: - import-from "^2.1.0" - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha512-eZ5H8rcgYazHbKC3PG4ClHNykCSxtAhxSSEM+2mb+7evD2CKF5V7c0dNum7AdpDh0ZdICwZY9sRSn8f+KH96sg== - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - -import-from@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" - integrity sha512-0vdnLL2wSGnhlRmzHJAg5JHjt1l2vYhzJ7tNLGbeVg0fse56tpGaH0uzH+r9Slej+BSXXEHvBKDEnVSLLE9/+w== - dependencies: - resolve-from "^3.0.0" - -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz#05698e3d45c88e8d7e9d92cb0584e77f096f3e43" - integrity sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A== - -import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== - dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha512-bup+4tap3Hympa+JBJUG7XuOsdNQ6fxt0MHyXMKuLBKn0OqsTfvUxkUrroEX1+B2VsSHvCjiIcZVxRtYa4nllA== - -infer-owner@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha512-8nWq2nLTAwd02jTqJExUYFSD/fKq6VH9Y/oG2accc/kdI0V98Bag8d5a4gi3XHz73rDWa2PvTtvcWYquKqSENA== - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - -ini@1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz#a09363e1911972ea16d7a8851005d84cf09a9a84" - integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== - -ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -internal-ip@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" - integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== - dependencies: - default-gateway "^4.2.0" - ipaddr.js "^1.9.0" - -internal-slot@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" - integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== - dependencies: - get-intrinsic "^1.2.0" - has "^1.0.3" - side-channel "^1.0.4" - -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== - -ip@^1.1.0, ip@^1.1.5: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.8.tgz#ae05948f6b075435ed3307acce04629da8cdbf48" - integrity sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg== - -ipaddr.js@1.9.1, ipaddr.js@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - integrity sha512-vOx7VprsKyllwjSkLV79NIhpyLfr3jAp7VaTCMXOJHu4m0Ew1CZ2fcjASwmV1jI3BWuWHB013M48eyeldk9gYg== - -is-absolute-url@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" - integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A== - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arguments@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" - integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.2.tgz#f2653ced8412081638ecb0ebbd0c41c6e0aecbbe" - integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.0" - is-typed-array "^1.1.10" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q== - dependencies: - binary-extensions "^1.0.0" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" - integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== - -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - -is-color-stop@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" - integrity sha512-H1U8Vz0cfXNujrJzEcvvwMDW9Ra+biSYA3ThdQvAnMLJkEHQXn6bWzLkxHtVYJ+Sdbx0b6finn3jZiaVe7MAHA== - dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" - -is-core-module@^2.13.0: - version "2.13.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" - integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== - dependencies: - has "^1.0.3" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg== - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha512-yVChGzahRFvbkscn2MlwGismPO12i9+znNruC5gVEntG3qu0xQMzsGg/JFbrsqDOHtHFPci+V5aP5T9I+yeKqw== - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw== - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-installed-globally@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz#fd3efa79ee670d1187233182d5b0a1dd00313141" - integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== - dependencies: - global-dirs "^2.0.1" - is-path-inside "^3.0.1" - -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-npm@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz#c90dd8380696df87a7a6d823c20d0b12bbe3c84d" - integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg== - dependencies: - kind-of "^3.0.2" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - -is-path-cwd@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - -is-path-in-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" - integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== - dependencies: - is-path-inside "^2.1.0" - -is-path-inside@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" - integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== - dependencies: - path-is-inside "^1.0.2" - -is-path-inside@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-plain-obj@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== - -is-plain-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" - integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== - -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-regex@^1.0.4, is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.12" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" - integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== - dependencies: - which-typed-array "^1.1.11" - -is-typedarray@^1.0.0, is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw== - -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz#d502d3382590ea3004893746754c89139973e232" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - -isarray@^2.0.1, isarray@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" - integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA== - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g== - -javascript-stringify@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" - integrity sha512-fnjC0up+0SjEJtgmmG+teeel68kutkvzfctO/KxE3qJlbunkJYAshgH3boU++gSBHP8z5/r0ts0qRIrHf0RTQQ== - -javascript-stringify@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-2.1.0.tgz#27c76539be14d8bd128219a2d731b09337904e79" - integrity sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg== - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg== - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" - integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA== - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ== - -json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.4.0.tgz#f7de4cf6efab838ebaeb3236474cbba5a1930ab5" - integrity sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA== - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== - -json5@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - integrity sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw== - -json5@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593" - integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== - dependencies: - minimist "^1.2.0" - -json5@^2.1.2, json5@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" - integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== - optionalDependencies: - graceful-fs "^4.1.6" - -jsprim@^1.2.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.2.tgz#712c65533a15c878ba59e9ed5f0e26d5b77c5feb" - integrity sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw== - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.4.0" - verror "1.10.0" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -killable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" - integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ== - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw== - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -last-call-webpack-plugin@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555" - integrity sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w== - dependencies: - lodash "^4.17.5" - webpack-sources "^1.1.0" - -latest-version@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== - dependencies: - package-json "^6.3.0" - -linkify-it@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" - integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw== - dependencies: - uc.micro "^1.0.1" - -load-script@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/load-script/-/load-script-1.0.0.tgz#0491939e0bee5643ee494a7e3da3d2bac70c6ca4" - integrity sha512-kPEjMFtZvwL9TaZo0uZ2ml+Ye9HUMmPwbYRJ324qF9tqMejwykJ5ggTyvzmrbBeapCAbk98BSbTeovHEEP1uCA== - -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@^0.2.16: - version "0.2.17" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-0.2.17.tgz#f86e6374d43205a6e6c60e9196f17c0299bfb348" - integrity sha512-tiv66G0SmiOx+pLWMtGEkfSEejxvb6N6uRrQjfWJIT79W9GMpgKeCAmm9aVBKtd4WEgntciI8CsGqjpDoCWJug== - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - object-assign "^4.0.1" - -loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3: - version "1.4.2" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.2.tgz#29a957f3a63973883eb684f10ffd3d151fec01a3" - integrity sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - -loader-utils@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.4.tgz#8b5cb38b5c34a9a018ee1fc0e6a066d1dfcc528c" - integrity sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash._reinterpolate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" - integrity sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA== - -lodash.clonedeep@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" - integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ== - -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== - -lodash.kebabcase@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz#8489b1cb0d29ff88195cceca448ff6d6cc295c36" - integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== - -lodash.template@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" - integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== - dependencies: - lodash._reinterpolate "^3.0.0" - lodash.templatesettings "^4.0.0" - -lodash.templatesettings@^4.0.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33" - integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ== - dependencies: - lodash._reinterpolate "^3.0.0" - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== - -lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.5: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -loglevel@^1.6.8: - version "1.8.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4" - integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg== - -lower-case@^1.1.1: - version "1.1.4" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" - integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA== - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^4.1.2: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -make-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w== - dependencies: - object-visit "^1.0.0" - -markdown-it-anchor@^5.0.2: - version "5.3.0" - resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.3.0.tgz#d549acd64856a8ecd1bea58365ef385effbac744" - integrity sha512-/V1MnLL/rgJ3jkMWo84UR+K+jF1cxNG1a+KwqeXqTIJ+jtA8aWSHuigx8lTzauiIjBDbwF3NcWQMotd0Dm39jA== - -markdown-it-chain@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/markdown-it-chain/-/markdown-it-chain-1.3.0.tgz#ccf6fe86c10266bafb4e547380dfd7f277cc17bc" - integrity sha512-XClV8I1TKy8L2qsT9iX3qiV+50ZtcInGXI80CA+DP62sMs7hXlyV/RM3hfwy5O3Ad0sJm9xIwQELgANfESo8mQ== - dependencies: - webpack-chain "^4.9.0" - -markdown-it-container@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/markdown-it-container/-/markdown-it-container-2.0.0.tgz#0019b43fd02eefece2f1960a2895fba81a404695" - integrity sha512-IxPOaq2LzrGuFGyYq80zaorXReh2ZHGFOB1/Hen429EJL1XkPI3FJTpx9TsJeua+j2qTru4h3W1TiCRdeivMmA== - -markdown-it-emoji@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/markdown-it-emoji/-/markdown-it-emoji-1.4.0.tgz#9bee0e9a990a963ba96df6980c4fddb05dfb4dcc" - integrity sha512-QCz3Hkd+r5gDYtS2xsFXmBYrgw6KuWcJZLCEkdfAuwzZbShCmCfta+hwAMq4NX/4xPzkSHduMKgMkkPUJxSXNg== - -markdown-it-table-of-contents@^0.4.0: - version "0.4.4" - resolved "https://registry.yarnpkg.com/markdown-it-table-of-contents/-/markdown-it-table-of-contents-0.4.4.tgz#3dc7ce8b8fc17e5981c77cc398d1782319f37fbc" - integrity sha512-TAIHTHPwa9+ltKvKPWulm/beozQU41Ab+FIefRaQV1NRnpzwcV9QOe6wXQS5WLivm5Q/nlo0rl6laGkMDZE7Gw== - -markdown-it@^8.4.1: - version "8.4.2" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54" - integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== - dependencies: - argparse "^1.0.7" - entities "~1.1.1" - linkify-it "^2.0.0" - mdurl "^1.0.1" - uc.micro "^1.0.5" - -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== - -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - -mdurl@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" - integrity sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== - -memory-fs@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - integrity sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -memory-fs@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" - integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== - -merge-source-map@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" - integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== - dependencies: - source-map "^0.6.1" - -merge2@^1.2.3: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -micromatch@^3.1.10, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.2: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mime@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.0.3, mime@^2.4.4: - version "2.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" - integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== - -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" - integrity sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ== - dependencies: - dom-walk "^0.1.0" - -mini-css-extract-plugin@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-0.6.0.tgz#a3f13372d6fcde912f3ee4cd039665704801e3b9" - integrity sha512-79q5P7YGI6rdnVyIAV4NXpBQJFWdkzJxCim3Kog4078fM0piAaFlwocqbejdWtLW1cEzCexPrh6EdyFsPgVdAw== - dependencies: - loader-utils "^1.1.0" - normalize-url "^2.0.1" - schema-utils "^1.0.0" - webpack-sources "^1.1.0" - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg== - -minimatch@^3.0.4, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" - integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== - -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.0.tgz#1bbf5ab1ba827af23575143490426455f481fe1e" - integrity sha512-OHsdUcVAQ6pOtg5JYWpCBo9W/GySVuwvP9hueRMW7UqshC0tbfzLv8wjySTPm3tfUZ/21CE9E1pJagOA91Pxew== - -mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.6, mkdirp@~0.5.1: - version "0.5.6" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" - integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== - dependencies: - minimist "^1.2.6" - -mkdirp@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - integrity sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ== - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3, ms@^2.1.1, ms@^2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - integrity sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ== - -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== - dependencies: - dns-packet "^1.3.1" - thunky "^1.0.2" - -nan@^2.12.1: - version "2.17.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.17.0.tgz#c0150a2368a182f033e9aa5195ec76ea41a199cb" - integrity sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ== - -nanoid@^3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" - integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -negotiator@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" - integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== - -neo-async@^2.5.0, neo-async@^2.6.1: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -no-case@^2.2.0: - version "2.3.2" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" - integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== - dependencies: - lower-case "^1.1.1" - -node-forge@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" - integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== - -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-releases@^2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" - integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== - -nopt@1.0.10: - version "1.0.10" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-1.0.10.tgz#6ddd21bd2a31417b92727dd585f8a6f37608ebee" - integrity sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg== - dependencies: - abbrev "1" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w== - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== - -normalize-url@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6" - integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== - dependencies: - prepend-http "^2.0.0" - query-string "^5.0.1" - sort-keys "^2.0.0" - -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - -normalize-url@^4.1.0: - version "4.5.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.1.tgz#0dd90cf1288ee1d1313b87081c9a5932ee48518a" - integrity sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA== - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw== - dependencies: - path-key "^2.0.0" - -nprogress@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" - integrity sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA== - -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -nth-check@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" - integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== - dependencies: - boolbase "^1.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - integrity sha512-Y1wZESM7VUThYY+4W+X4ySH2maqcA+p7UR+w8VWNWVAd6lwuXXWz/w/Cz43J/dI2I+PS6wD5N+bJUF+gjWvIqg== - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ== - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-inspect@^1.12.3, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== - -object-is@^1.0.1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -object-keys@^1.0.11, object-keys@^1.1.0, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA== - dependencies: - isobject "^3.0.0" - -object.assign@^4.1.0, object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0: - version "2.1.6" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz#5e5c384dd209fa4efffead39e3a0512770ccc312" - integrity sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ== - dependencies: - array.prototype.reduce "^1.0.5" - call-bind "^1.0.2" - define-properties "^1.2.0" - es-abstract "^1.21.2" - safe-array-concat "^1.0.0" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== - dependencies: - isobject "^3.0.1" - -object.values@^1.1.0: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - -on-finished@2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -opencollective-postinstall@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" - integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== - -opn@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" - integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== - dependencies: - is-wsl "^1.1.0" - -optimize-css-assets-webpack-plugin@^5.0.1: - version "5.0.8" - resolved "https://registry.yarnpkg.com/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.8.tgz#cbccdcf5a6ef61d4f8cc78cf083a67446e5f402a" - integrity sha512-mgFS1JdOtEGzD8l+EuISqL57cKO+We9GcoiQEmdCWRqqck+FGNmYJtx9qfAPzEz+lRrlThWMuGDaRkI/yWNx/Q== - dependencies: - cssnano "^4.1.10" - last-call-webpack-plugin "^3.0.0" - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== - -p-limit@^2.0.0, p-limit@^2.2.0, p-limit@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-map@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== - -p-retry@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" - integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== - dependencies: - retry "^0.12.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz#6feedaca35e75725876d0b0e64974697fed145b0" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== - dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" - -pako@~1.0.5: - version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - -parallel-transform@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" - integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== - dependencies: - cyclist "^1.0.1" - inherits "^2.0.3" - readable-stream "^2.1.5" - -param-case@2.1.x: - version "2.1.1" - resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" - integrity sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w== - dependencies: - no-case "^2.2.0" - -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw== - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw== - -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q== - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w== - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== - -path-type@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" - integrity sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg== - dependencies: - pify "^3.0.0" - -pbkdf2@^3.0.3: - version "3.1.2" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" - integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== - -picocolors@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-0.2.1.tgz#570670f793646851d1ba135996962abad587859f" - integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -pkg-dir@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -portfinder@^1.0.13, portfinder@^1.0.26: - version "1.0.32" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81" - integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg== - dependencies: - async "^2.6.4" - debug "^3.2.7" - mkdirp "^0.5.6" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg== - -postcss-calc@^7.0.1: - version "7.0.5" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.5.tgz#f8a6e99f12e619c2ebc23cf6c486fdc15860933e" - integrity sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg== - dependencies: - postcss "^7.0.27" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.2" - -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" - -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" - -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" - -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" - -postcss-load-config@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.1.2.tgz#c5ea504f2c4aef33c7359a34de3573772ad7502a" - integrity sha512-/rDeGV6vMUo3mwJZmeHfEDvwnTKKqQ0S7OHUi/kJvvtx3aWtyWG2/0ZWnzCt2keEclwN6Tf0DST2v9kITdOKYw== - dependencies: - cosmiconfig "^5.0.0" - import-cwd "^2.0.0" - -postcss-loader@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-3.0.0.tgz#6b97943e47c72d845fa9e03f273773d4e8dd6c2d" - integrity sha512-cLWoDEY5OwHcAjDnkyRQzAXfs2jrKjXpO/HQFcc5b5u/r7aa471wdmChmwfnv7x2u840iat/wi0lQ5nbRgSkUA== - dependencies: - loader-utils "^1.1.0" - postcss "^7.0.0" - postcss-load-config "^2.0.0" - schema-utils "^1.0.0" - -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" - -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" - -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== - dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" - -postcss-modules-local-by-default@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-2.0.6.tgz#dd9953f6dd476b5fd1ef2d8830c8929760b56e63" - integrity sha512-oLUV5YNkeIBa0yQl7EYnxMgy4N6noxmiwZStaEJUSe2xPMcdNc8WmBQuQCx18H5psYbVxz8zoHk0RAAYZXP9gA== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - postcss-value-parser "^3.3.1" - -postcss-modules-scope@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" - integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - -postcss-modules-values@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-2.0.0.tgz#479b46dc0c5ca3dc7fa5270851836b9ec7152f64" - integrity sha512-Ki7JZa7ff1N3EIMlPnGTZfUMe69FFwiQPnVSXC9mnn3jozCRBYIxiZd44yJOV2AmabOo4qFf8s0dC/+lweG7+w== - dependencies: - icss-replace-symbols "^1.1.0" - postcss "^7.0.6" - -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" - -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-safe-parser@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.2.tgz#a6d4e48f0f37d9f7c11b2a581bf00f8ba4870b96" - integrity sha512-Uw6ekxSWNLCPesSv/cmqf2bY/77z11O7jZGPax3ycZMFU/oi2DMH9i89AdHc1tRwFg/arFoEwX0IS3LCUxJh1g== - dependencies: - postcss "^7.0.26" - -postcss-selector-parser@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" - integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== - dependencies: - dot-prop "^5.2.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: - version "6.0.13" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" - integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - -postcss-svgo@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.3.tgz#343a2cdbac9505d416243d496f724f38894c941e" - integrity sha512-NoRbrcMWTtUghzuKSoIm6XV+sJdvZ7GZSc3wdBN0W19FTtp2ko8NqLsgoh/m9CzNhU3KLPvQmjIwtaNFkaFTvw== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" - -postcss-unique-selectors@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== - dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.0, postcss-value-parser@^3.3.0, postcss-value-parser@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - -postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" - integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== - -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.26, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.36, postcss@^7.0.5, postcss@^7.0.6: - version "7.0.39" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.39.tgz#9624375d965630e2e1f2c02a935c82a59cb48309" - integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== - dependencies: - picocolors "^0.2.1" - source-map "^0.6.1" - -postcss@^8.4.14: - version "8.4.27" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.27.tgz#234d7e4b72e34ba5a92c29636734349e0d9c3057" - integrity sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ== - dependencies: - nanoid "^3.3.6" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== - -"prettier@^1.18.2 || ^2.0.0": - version "2.8.8" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da" - integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q== - -pretty-error@^2.0.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/pretty-error/-/pretty-error-2.1.2.tgz#be89f82d81b1c86ec8fdfbc385045882727f93b6" - integrity sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw== - dependencies: - lodash "^4.17.20" - renderkid "^2.0.4" - -pretty-time@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pretty-time/-/pretty-time-1.1.0.tgz#ffb7429afabb8535c346a34e41873adf3d74dd0e" - integrity sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA== - -prismjs@^1.13.0, prismjs@^1.20.0: - version "1.29.0" - resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.29.0.tgz#f113555a8fa9b57c35e637bba27509dcf802dd12" - integrity sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q== - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== - -psl@^1.1.28: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@^1.2.4, punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ== - -punycode@^2.1.0, punycode@^2.1.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== - -pupa@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz#f5e8fd4afc2c5d97828faa523549ed8744a20d62" - integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== - dependencies: - escape-goat "^2.0.0" - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== - -qs@6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - -qs@^6.11.0: - version "6.11.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" - integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== - dependencies: - side-channel "^1.0.4" - -qs@~6.5.2: - version "6.5.3" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad" - integrity sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA== - -query-string@^5.0.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-5.1.1.tgz#a78c012b71c17e05f2e3fa2319dd330682efb3cb" - integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== - dependencies: - decode-uri-component "^0.2.0" - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -querystring-es3@^0.2.0, querystring-es3@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA== - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - -rc@1.2.8, rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.0.6, readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -reduce@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/reduce/-/reduce-1.0.2.tgz#0cd680ad3ffe0b060e57a5c68bdfce37168d361b" - integrity sha512-xX7Fxke/oHO5IfZSk77lvPa/7bjMh9BuCk4OOoX5XTXrM7s0Z+MkPfSDfz0q7r91BhhGSs8gii/VEN/7zhCPpQ== - dependencies: - object-keys "^1.1.0" - -regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.14.0: - version "0.14.0" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" - integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== - -regenerator-transform@^0.15.2: - version "0.15.2" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" - integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== - dependencies: - "@babel/runtime" "^7.8.4" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexp.prototype.flags@^1.2.0, regexp.prototype.flags@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" - integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.2.0" - functions-have-names "^1.2.3" - -regexpu-core@^5.3.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" - integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== - dependencies: - "@babel/regjsgen" "^0.8.0" - regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsparser "^0.9.1" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.1.0" - -registry-auth-token@^4.0.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.2.tgz#f02d49c3668884612ca031419491a13539e21fac" - integrity sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg== - dependencies: - rc "1.2.8" - -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz#e98334b50d5434b81136b44ec638d9c2009c5009" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== - dependencies: - rc "^1.2.8" - -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== - dependencies: - jsesc "~0.5.0" - -relateurl@0.2.x: - version "0.2.7" - resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" - integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw== - -renderkid@^2.0.4: - version "2.0.7" - resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.7.tgz#464f276a6bdcee606f4a15993f9b29fc74ca8609" - integrity sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ== - dependencies: - css-select "^4.1.3" - dom-converter "^0.2.0" - htmlparser2 "^6.1.0" - lodash "^4.17.21" - strip-ansi "^3.0.1" - -repeat-element@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.4.tgz#be681520847ab58c7568ac75fbfad28ed42d39e9" - integrity sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w== - -request@^2.87.0: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - dependencies: - aws-sign2 "~0.7.0" - aws4 "^1.8.0" - caseless "~0.12.0" - combined-stream "~1.0.6" - extend "~3.0.2" - forever-agent "~0.6.1" - form-data "~2.3.2" - har-validator "~5.1.3" - http-signature "~1.2.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.19" - oauth-sign "~0.9.0" - performance-now "^2.1.0" - qs "~6.5.2" - safe-buffer "^5.1.2" - tough-cookie "~2.5.0" - tunnel-agent "^0.6.0" - uuid "^3.3.2" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== - -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - integrity sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg== - dependencies: - resolve-from "^3.0.0" - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw== - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg== - -resolve@^1.14.2, resolve@^1.22.0: - version "1.22.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" - integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== - dependencies: - is-core-module "^2.13.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ== - dependencies: - lowercase-keys "^1.0.0" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== - -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" - integrity sha512-gDK5mkALDFER2YLqH6imYvK6g02gpNGM4ILDZ472EwWfXZnC2ZEpoB2ECXTyOVUKuk/bPJZMzwQPBYICzP+D3w== - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" - integrity sha512-zgn5OjNQXLUTdq8m17KdaicF6w89TZs8ZU8y0AYENIU6wG8GG6LLm0yLSiPY8DmaYmHdgRW8rnApjoT0fQRfMg== - -rimraf@^2.5.4, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - integrity sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg== - dependencies: - aproba "^1.1.1" - -safe-array-concat@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" - integrity sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.0" - has-symbols "^1.0.3" - isarray "^2.0.5" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" - is-regex "^1.1.4" - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg== - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sax@~1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== - dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" - -schema-utils@^2.6.5: - version "2.7.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" - integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== - dependencies: - "@types/json-schema" "^7.0.5" - ajv "^6.12.4" - ajv-keywords "^3.5.2" - -section-matter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" - integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== - dependencies: - extend-shallow "^2.0.1" - kind-of "^6.0.0" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== - -selfsigned@^1.10.8: - version "1.10.14" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.14.tgz#ee51d84d9dcecc61e07e4aba34f229ab525c1574" - integrity sha512-lkjaiAye+wBZDCBsu5BGi0XiLRxeUlsGod5ZP924CRSEoGuZAw/f7y9RKu28rwTfiHVhdavhB0qH0INV6P1lEA== - dependencies: - node-forge "^0.10.0" - -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz#05f77ce59f325e00e2706afd67bb506ddb1ca32b" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== - dependencies: - semver "^6.3.0" - -semver@^5.1.0, semver@^5.5.0, semver@^5.6.0: - version "5.7.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@^6.0.0, semver@^6.1.0, semver@^6.2.0, semver@^6.3.0, semver@^6.3.1: - version "6.3.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -send@0.18.0: - version "0.18.0" - resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== - dependencies: - randombytes "^2.1.0" - -serialize-javascript@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== - dependencies: - randombytes "^2.1.0" - -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setimmediate@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" - integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg== - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha512-3TYDR7xWt4dIqV2JauJr+EJeW356RXijHeUlO+8djJ+uBXPn8/2dpzBc8yQhh583sVvc9CvFAeQVgijsH+PNNg== - -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - -smoothscroll-polyfill@^0.4.3: - version "0.4.4" - resolved "https://registry.yarnpkg.com/smoothscroll-polyfill/-/smoothscroll-polyfill-0.4.4.tgz#3a259131dc6930e6ca80003e1cb03b603b69abf8" - integrity sha512-TK5ZA9U5RqCwMpfoMq/l1mrH0JAR7y7KRvOBx0n2869aLxch+gT9GhN3yUfjiw+d/DiF1mKo14+hd62JyMmoBg== - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -sockjs-client@^1.5.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.6.1.tgz#350b8eda42d6d52ddc030c39943364c11dcad806" - integrity sha512-2g0tjOR+fRs0amxENLi/q5TiJTqY+WXFOzb5UwXndlK6TO3U/mirZznpx6w34HVMoc3g7cY24yC/ZMIYnDlfkw== - dependencies: - debug "^3.2.7" - eventsource "^2.0.2" - faye-websocket "^0.11.4" - inherits "^2.0.4" - url-parse "^1.5.10" - -sockjs@^0.3.21: - version "0.3.24" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" - integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== - dependencies: - faye-websocket "^0.11.3" - uuid "^8.3.2" - websocket-driver "^0.7.4" - -sort-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" - integrity sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg== - dependencies: - is-plain-obj "^1.0.0" - -source-list-map@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== - -source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@~0.5.12: - version "0.5.21" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" - integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA== - -source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@^0.7.3: - version "0.7.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" - integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== - -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" - -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== - dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - -sshpk@^1.7.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.17.0.tgz#578082d92d4fe612b13007496e543fa0fbcbe4c5" - integrity sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ== - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - bcrypt-pbkdf "^1.0.0" - dashdash "^1.12.0" - ecc-jsbn "~0.1.1" - getpass "^0.1.1" - jsbn "~0.1.0" - safer-buffer "^2.0.2" - tweetnacl "~0.14.0" - -ssri@^6.0.1: - version "6.0.2" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.2.tgz#157939134f20464e7301ddba3e90ffa8f7728ac5" - integrity sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q== - dependencies: - figgy-pudding "^3.5.1" - -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -stack-utils@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.5.tgz#a19b0b01947e0029c8e451d5d61a498f5bb1471b" - integrity sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ== - dependencies: - escape-string-regexp "^2.0.0" - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g== - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -statuses@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" - integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== - -"statuses@>= 1.4.0 < 2": - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -std-env@^2.2.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/std-env/-/std-env-2.3.1.tgz#d42271908819c243f8defc77a140fc1fcee336a1" - integrity sha512-eOsoKTWnr6C8aWrqJJ2KAReXoa7Vn5Ywyw6uCXgA/xDhxPoaIsBa5aNJmISY04dLwXPBnDHW4diGM7Sn5K4R/g== - dependencies: - ci-info "^3.1.1" - -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ== - -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.0.0, string-width@^4.1.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string.prototype.trim@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" - integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -string_decoder@^1.0.0, string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q== - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== - -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -stylus-loader@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-3.0.2.tgz#27a706420b05a38e038e7cacb153578d450513c6" - integrity sha512-+VomPdZ6a0razP+zinir61yZgpw2NfljeSsdUF5kJuEzlo3khXhY19Fn6l8QQz1GRJGtMCo8nG5C04ePyV7SUA== - dependencies: - loader-utils "^1.0.2" - lodash.clonedeep "^4.5.0" - when "~3.6.x" - -stylus@^0.54.8: - version "0.54.8" - resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" - integrity sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg== - dependencies: - css-parse "~2.0.0" - debug "~3.1.0" - glob "^7.1.6" - mkdirp "~1.0.4" - safer-buffer "^2.1.2" - sax "~1.2.4" - semver "^6.3.0" - source-map "^0.7.3" - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -svg-tags@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" - integrity sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA== - -svgo@^1.0.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" - integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.37" - csso "^4.0.2" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - -tapable@^1.0.0, tapable@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - -term-size@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz#2a6a54840432c2fb6320fea0f415531e90189f54" - integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== - -terser-webpack-plugin@^1.4.3: - version "1.4.5" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" - integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" - -terser@^4.1.2: - version "4.8.1" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.1.tgz#a00e5634562de2239fd404c649051bf6fc21144f" - integrity sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw== - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -through@~2.3.4: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== - -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - -timers-browserify@^2.0.4: - version "2.0.12" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" - integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== - dependencies: - setimmediate "^1.0.4" - -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A== - -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA== - -to-factory@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-factory/-/to-factory-1.0.0.tgz#8738af8bd97120ad1d4047972ada5563bf9479b1" - integrity sha512-JVYrY42wMG7ddf+wBUQR/uHGbjUHZbLisJ8N62AMm0iTZ0p8YTcZLzdtomU0+H+wa99VbkyvQGB3zxB7NDzgIQ== - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg== - dependencies: - kind-of "^3.0.2" - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg== - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -toidentifier@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" - integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== - -toml@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" - integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== - -toposort@^1.0.0: - version "1.0.7" - resolved "https://registry.yarnpkg.com/toposort/-/toposort-1.0.7.tgz#2e68442d9f64ec720b8cc89e6443ac6caa950029" - integrity sha512-FclLrw8b9bMWf4QlCJuHBEVhSRsqDj6u3nIjAzPeJvgl//1hBlffdlk0MALceL14+koWEdU4ofRAXofbODxQzg== - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw== - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w== - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -typed-array-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" - integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.1" - is-typed-array "^1.1.10" - -typed-array-byte-length@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" - integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" - -typed-array-byte-offset@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" - integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - has-proto "^1.0.1" - is-typed-array "^1.1.10" - -typed-array-length@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" - integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== - dependencies: - call-bind "^1.0.2" - for-each "^0.3.3" - is-typed-array "^1.1.9" - -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA== - -uc.micro@^1.0.1, uc.micro@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" - integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== - -uglify-js@3.4.x: - version "3.4.10" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f" - integrity sha512-Y2VsbPVs0FIshJztycsO2SfPk7/KAF/T72qzv9u5EpQ4kB2hQoHlhNQTsNyy6ul7lQtqJN/AoWeS23OzEiEFxw== - dependencies: - commander "~2.19.0" - source-map "~0.6.1" - -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" - integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" - integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA== - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - integrity sha512-mZdDpf3vBV5Efh29kMw5tXoup/buMgxLzOt/XKFKcVmi+15ManNQWr6HfZ2aiZTYlYixbdNJ0KFmIZIv52tHSQ== - -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - dependencies: - crypto-random-string "^2.0.0" - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== - -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg== - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ== - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -upath@^1.1.0, upath@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -update-notifier@^4.0.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz#be86ee13e8ce48fb50043ff72057b5bd598e1ea3" - integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== - dependencies: - boxen "^4.2.0" - chalk "^3.0.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.3.1" - is-npm "^4.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.0.0" - pupa "^2.0.1" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" - -upper-case@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" - integrity sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg== - -url-loader@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-1.1.2.tgz#b971d191b83af693c5e3fea4064be9e1f2d7f8d8" - integrity sha512-dXHkKmw8FhPqu8asTc1puBfe3TehOCo2+RmOOev5suNCIYBcT626kxiWg1NBVkwc4rO8BGa7gP70W7VXuqHrjg== - dependencies: - loader-utils "^1.1.0" - mime "^2.0.3" - schema-utils "^1.0.0" - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ== - dependencies: - prepend-http "^2.0.0" - -url-parse@^1.5.10: - version "1.5.10" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.10.tgz#9d3c2f736c1d75dd3bd2be507dcc111f1e2ea9c1" - integrity sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -url@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.1.tgz#26f90f615427eca1b9f4d6a28288c147e2302a32" - integrity sha512-rWS3H04/+mzzJkv0eZ7vEDGiQbgquI1fGfOad6zKvgYQi1SzMmhl7c/DdRGxhaWrVH6z0qWITo8rpnxK/RfEhA== - dependencies: - punycode "^1.4.1" - qs "^6.11.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -util.promisify@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.0.tgz#440f7165a459c9a16dc145eb8e72f35687097030" - integrity sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA== - dependencies: - define-properties "^1.1.2" - object.getownpropertydescriptors "^2.0.3" - -util.promisify@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha512-5KiHfsmkqacuKjkRkdV7SsfDJ2EGiPsK92s2MhNSY0craxjTdKTtqKsJaCWp4LW33ZZ0OPUv1WO/TFvNQRiQxQ== - dependencies: - inherits "2.0.1" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - -utila@~0.4: - version "0.4.0" - resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" - integrity sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA== - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== - -uuid@^3.3.2: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== - -vendors@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" - integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw== - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vm-browserify@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - -vue-hot-reload-api@^2.3.0: - version "2.3.4" - resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.3.4.tgz#532955cc1eb208a3d990b3a9f9a70574657e08f2" - integrity sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog== - -vue-loader@^15.7.1: - version "15.10.1" - resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-15.10.1.tgz#c451c4cd05a911aae7b5dbbbc09fb913fb3cca18" - integrity sha512-SaPHK1A01VrNthlix6h1hq4uJu7S/z0kdLUb6klubo738NeQoLbS6V9/d8Pv19tU0XdQKju3D1HSKuI8wJ5wMA== - dependencies: - "@vue/component-compiler-utils" "^3.1.0" - hash-sum "^1.0.2" - loader-utils "^1.1.0" - vue-hot-reload-api "^2.3.0" - vue-style-loader "^4.1.0" - -vue-router@^3.4.5: - version "3.6.5" - resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.6.5.tgz#95847d52b9a7e3f1361cb605c8e6441f202afad8" - integrity sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ== - -vue-server-renderer@^2.6.10: - version "2.7.14" - resolved "https://registry.yarnpkg.com/vue-server-renderer/-/vue-server-renderer-2.7.14.tgz#986f3fdca63fbb38bb6834698f11e0d6a81f182f" - integrity sha512-NlGFn24tnUrj7Sqb8njhIhWREuCJcM3140aMunLNcx951BHG8j3XOrPP7psSCaFA8z6L4IWEjudztdwTp1CBVw== - dependencies: - chalk "^4.1.2" - hash-sum "^2.0.0" - he "^1.2.0" - lodash.template "^4.5.0" - lodash.uniq "^4.5.0" - resolve "^1.22.0" - serialize-javascript "^6.0.0" - source-map "0.5.6" - -vue-style-loader@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-4.1.3.tgz#6d55863a51fa757ab24e89d9371465072aa7bc35" - integrity sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg== - dependencies: - hash-sum "^1.0.2" - loader-utils "^1.0.2" - -vue-template-compiler@^2.6.10: - version "2.7.14" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz#4545b7dfb88090744c1577ae5ac3f964e61634b1" - integrity sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ== - dependencies: - de-indent "^1.0.2" - he "^1.2.0" - -vue-template-es2015-compiler@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825" - integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw== - -vue@^2.6.10: - version "2.7.14" - resolved "https://registry.yarnpkg.com/vue/-/vue-2.7.14.tgz#3743dcd248fd3a34d421ae456b864a0246bafb17" - integrity sha512-b2qkFyOM0kwqWFuQmgd4o+uHGU7T+2z3T+WQp8UBjADfEv2n4FEMffzBmCKNP0IGzOEEfYjvtcC62xaSKeQDrQ== - dependencies: - "@vue/compiler-sfc" "2.7.14" - csstype "^3.1.0" - -vuepress-html-webpack-plugin@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/vuepress-html-webpack-plugin/-/vuepress-html-webpack-plugin-3.2.0.tgz#219be272ad510faa8750d2d4e70fd028bfd1c16e" - integrity sha512-BebAEl1BmWlro3+VyDhIOCY6Gef2MCBllEVAP3NUAtMguiyOwo/dClbwJ167WYmcxHJKLl7b0Chr9H7fpn1d0A== - dependencies: - html-minifier "^3.2.3" - loader-utils "^0.2.16" - lodash "^4.17.3" - pretty-error "^2.0.2" - tapable "^1.0.0" - toposort "^1.0.0" - util.promisify "1.0.0" - -vuepress-plugin-container@^2.0.2: - version "2.1.5" - resolved "https://registry.yarnpkg.com/vuepress-plugin-container/-/vuepress-plugin-container-2.1.5.tgz#37fff05662fedbd63ffd3a5463b2592c7a7f3133" - integrity sha512-TQrDX/v+WHOihj3jpilVnjXu9RcTm6m8tzljNJwYhxnJUW0WWQ0hFLcDTqTBwgKIFdEiSxVOmYE+bJX/sq46MA== - dependencies: - "@vuepress/shared-utils" "^1.2.0" - markdown-it-container "^2.0.0" - -vuepress-plugin-smooth-scroll@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/vuepress-plugin-smooth-scroll/-/vuepress-plugin-smooth-scroll-0.0.3.tgz#6eff2d4c186cca917cc9f7df2b0af7de7c8c6438" - integrity sha512-qsQkDftLVFLe8BiviIHaLV0Ea38YLZKKonDGsNQy1IE0wllFpFIEldWD8frWZtDFdx6b/O3KDMgVQ0qp5NjJCg== - dependencies: - smoothscroll-polyfill "^0.4.3" - -vuepress@^1.5.0: - version "1.9.10" - resolved "https://registry.yarnpkg.com/vuepress/-/vuepress-1.9.10.tgz#14700fccd85bc56c810cf367ecc797fcbecc7c0f" - integrity sha512-UnGm9vjQvG918SZVNvgiUlNimLqawdYPq0aPRXDpEB1VksvqegVFy/GKdA8ShXJaEpOMPSt7YD4uK21jaMs3kA== - dependencies: - "@vuepress/core" "1.9.10" - "@vuepress/theme-default" "1.9.10" - "@vuepress/types" "1.9.10" - cac "^6.5.6" - envinfo "^7.2.0" - opencollective-postinstall "^2.0.2" - update-notifier "^4.0.0" - -watchpack-chokidar2@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" - integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== - dependencies: - graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.1" - watchpack-chokidar2 "^2.0.1" - -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - -webpack-chain@^4.9.0: - version "4.12.1" - resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-4.12.1.tgz#6c8439bbb2ab550952d60e1ea9319141906c02a6" - integrity sha512-BCfKo2YkDe2ByqkEWe1Rw+zko4LsyS75LVr29C6xIrxAg9JHJ4pl8kaIZ396SUSNp6b4815dRZPSTAS8LlURRQ== - dependencies: - deepmerge "^1.5.2" - javascript-stringify "^1.6.0" - -webpack-chain@^6.0.0: - version "6.5.1" - resolved "https://registry.yarnpkg.com/webpack-chain/-/webpack-chain-6.5.1.tgz#4f27284cbbb637e3c8fbdef43eef588d4d861206" - integrity sha512-7doO/SRtLu8q5WM0s7vPKPWX580qhi0/yBHkOxNkv50f6qB76Zy9o2wRTrrPULqYTvQlVHuvbA8v+G5ayuUDsA== - dependencies: - deepmerge "^1.5.2" - javascript-stringify "^2.0.1" - -webpack-dev-middleware@^3.7.2: - version "3.7.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" - integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== - dependencies: - memory-fs "^0.4.1" - mime "^2.4.4" - mkdirp "^0.5.1" - range-parser "^1.2.1" - webpack-log "^2.0.0" - -webpack-dev-server@^3.5.1: - version "3.11.3" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.3.tgz#8c86b9d2812bf135d3c9bce6f07b718e30f7c3d3" - integrity sha512-3x31rjbEQWKMNzacUZRE6wXvUFuGpH7vr0lIEbYpMAG9BOxi0928QU1BBswOAP3kg3H1O4hiS+sq4YyAn6ANnA== - dependencies: - ansi-html-community "0.0.8" - bonjour "^3.5.0" - chokidar "^2.1.8" - compression "^1.7.4" - connect-history-api-fallback "^1.6.0" - debug "^4.1.1" - del "^4.1.1" - express "^4.17.1" - html-entities "^1.3.1" - http-proxy-middleware "0.19.1" - import-local "^2.0.0" - internal-ip "^4.3.0" - ip "^1.1.5" - is-absolute-url "^3.0.3" - killable "^1.0.1" - loglevel "^1.6.8" - opn "^5.5.0" - p-retry "^3.0.1" - portfinder "^1.0.26" - schema-utils "^1.0.0" - selfsigned "^1.10.8" - semver "^6.3.0" - serve-index "^1.9.1" - sockjs "^0.3.21" - sockjs-client "^1.5.0" - spdy "^4.0.2" - strip-ansi "^3.0.1" - supports-color "^6.1.0" - url "^0.11.0" - webpack-dev-middleware "^3.7.2" - webpack-log "^2.0.0" - ws "^6.2.1" - yargs "^13.3.2" - -webpack-log@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" - integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== - dependencies: - ansi-colors "^3.0.0" - uuid "^3.3.2" - -webpack-merge@^4.1.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" - integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== - dependencies: - lodash "^4.17.15" - -webpack-sources@^1.1.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@^4.8.1: - version "4.46.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.46.0.tgz#bf9b4404ea20a073605e0a011d188d77cb6ad542" - integrity sha512-6jJuJjg8znb/xRItk7bkT0+Q7AHCYjjFnvKIWQPkNIOyRqoCGvkOs0ipeQzrqz4l5FtN5ZI/ukEHroeX/o1/5Q== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.5.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.7.4" - webpack-sources "^1.4.1" - -webpackbar@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/webpackbar/-/webpackbar-3.2.0.tgz#bdaad103fad11a4e612500e72aaae98b08ba493f" - integrity sha512-PC4o+1c8gWWileUfwabe0gqptlXUDJd5E0zbpr2xHP1VSOVlZVPBZ8j6NCR8zM5zbKdxPhctHXahgpNK1qFDPw== - dependencies: - ansi-escapes "^4.1.0" - chalk "^2.4.1" - consola "^2.6.0" - figures "^3.0.0" - pretty-time "^1.1.0" - std-env "^2.2.1" - text-table "^0.2.0" - wrap-ansi "^5.1.0" - -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - -when@~3.6.x: - version "3.6.4" - resolved "https://registry.yarnpkg.com/when/-/when-3.6.4.tgz#473b517ec159e2b85005497a13983f095412e34e" - integrity sha512-d1VUP9F96w664lKINMGeElWdhhb5sC+thXM+ydZGU3ZnaE09Wv6FaS+mpM9570kcDs/xMfcXJBTLsMdHEFYY9Q== - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which-module@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" - integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== - -which-typed-array@^1.1.10, which-typed-array@^1.1.11: - version "1.1.11" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a" - integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== - dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" - -which@^1.2.9: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -widest-line@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz#8292333bbf66cb45ff0de1603b136b7ae1496eca" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== - dependencies: - string-width "^4.0.0" - -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== - dependencies: - errno "~0.1.7" - -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz#56bd5c5a5c70481cd19c571bd39ab965a5de56e8" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== - dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - -ws@^6.2.1: - version "6.2.2" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" - integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== - dependencies: - async-limiter "~1.0.0" - -xdg-basedir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz#4bc8d9984403696225ef83a1573cbbcb4e79db13" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== - -xtend@^4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" - integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ== - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== - -yallist@^3.0.2: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yargs-parser@^13.1.2: - version "13.1.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" - integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs@^13.3.2: - version "13.3.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" - integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.2" - -zepto@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/zepto/-/zepto-1.2.0.tgz#e127bd9e66fd846be5eab48c1394882f7c0e4f98" - integrity sha512-C1x6lfvBICFTQIMgbt3JqMOno3VOtkWat/xEakLTOurskYIHPmzJrzd1e8BnmtdDVJlGuk5D+FxyCA8MPmkIyA== From 130219e0e7133893796f2064a72e3cd88227cb4a Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 5 Mar 2025 13:38:18 +0800 Subject: [PATCH 301/941] Prepare version 9.0.0-beta9 --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index db2671a55..f5e20071f 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased] + +## [v9.0.0-beta10] (2025-03-05) + **Added** - Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) diff --git a/gradle.properties b/gradle.properties index 69c60af1a..84b2c4aee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta10 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 6fff3030edd01445663bc286881bc1bfd9e5414f Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 5 Mar 2025 13:39:09 +0800 Subject: [PATCH 302/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 84b2c4aee..69c60af1a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta10 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From daf664fc1719461a94c4362f82ec89461fafa002 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 5 Mar 2025 16:44:31 +0800 Subject: [PATCH 303/941] Enable Gemini review but disable summary --- .gemini/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gemini/config.yaml b/.gemini/config.yaml index 2c3813212..fc1f1d49b 100644 --- a/.gemini/config.yaml +++ b/.gemini/config.yaml @@ -1,6 +1,6 @@ have_fun: true code_review: - disable: true + disable: false comment_severity_threshold: MEDIUM max_review_comments: -1 pull_request_opened: From c8b6991ec17799a5a0b8004e0122775e04dc0b18 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 5 Mar 2025 08:29:31 -0500 Subject: [PATCH 304/941] Polish ShadowSpec (#1307) * Fix overloads of ShadowJar.transform * Overload relocate * Overload append * Update changelog * Remove throwing * Rearrange * Remove ShadowSpec from return values * Move KClass overloads into ShadowJar to make them reified * Rearrange --- api/shadow.api | 98 ++++++++--------- docs/changes/README.md | 7 ++ lint-baseline.xml | 2 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 102 ++++++++---------- .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 80 +++++++------- 5 files changed, 134 insertions(+), 155 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 60f2346d9..e513aa7fc 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -171,14 +171,10 @@ public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAc public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar : org/gradle/api/tasks/bundling/Jar, com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec { public fun ()V - public fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun append (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun append (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun append (Ljava/lang/String;Ljava/lang/String;)V protected fun copy ()V protected fun createCopyAction ()Lorg/gradle/api/internal/file/copy/CopyAction; - public fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun dependencies (Lorg/gradle/api/Action;)V public fun getApiJars ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getConfigurations ()Lorg/gradle/api/provider/SetProperty; public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; @@ -194,54 +190,48 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getSourceSetsClassesDirs ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getToMinimize ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getTransformers ()Lorg/gradle/api/provider/SetProperty; - public fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun mergeServiceFiles (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun mergeServiceFiles (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun mergeServiceFiles (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun mergeServiceFiles (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun minimize ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun minimize ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun minimize (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun minimize (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun relocate (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun relocate (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar; - public synthetic fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; -} - -public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec : org/gradle/api/file/CopySpec { - public abstract fun append (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun append (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun dependencies (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun mergeGroovyExtensionModules ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun mergeServiceFiles ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun mergeServiceFiles (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun mergeServiceFiles (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun minimize ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun minimize (Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun relocate (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun relocate (Ljava/lang/String;Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun transform (Ljava/lang/Class;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; - public abstract fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec; + public fun mergeGroovyExtensionModules ()V + public fun mergeServiceFiles ()V + public fun mergeServiceFiles (Ljava/lang/String;)V + public fun mergeServiceFiles (Lorg/gradle/api/Action;)V + public fun minimize ()V + public fun minimize (Lorg/gradle/api/Action;)V + public fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;Lorg/gradle/api/Action;)V + public fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)V + public fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;Lorg/gradle/api/Action;)V + public fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)V +} + +public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec { + public fun append (Ljava/lang/String;)V + public abstract fun append (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun dependencies (Lorg/gradle/api/Action;)V + public abstract fun mergeGroovyExtensionModules ()V + public abstract fun mergeServiceFiles ()V + public abstract fun mergeServiceFiles (Ljava/lang/String;)V + public abstract fun mergeServiceFiles (Lorg/gradle/api/Action;)V + public abstract fun minimize ()V + public abstract fun minimize (Lorg/gradle/api/Action;)V + public fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)V + public abstract fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;Lorg/gradle/api/Action;)V + public fun relocate (Ljava/lang/Class;)V + public abstract fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)V + public fun relocate (Ljava/lang/String;Ljava/lang/String;)V + public abstract fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)V + public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;Lorg/gradle/api/Action;)V + public fun transform (Ljava/lang/Class;)V + public abstract fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec$DefaultImpls { + public static fun append (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Ljava/lang/String;)V + public static fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)V + public static fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Ljava/lang/Class;)V + public static fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Ljava/lang/String;Ljava/lang/String;)V + public static fun transform (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)V + public static fun transform (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Ljava/lang/Class;)V } public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { diff --git a/docs/changes/README.md b/docs/changes/README.md index f5e20071f..1de957574 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,13 @@ ## [Unreleased] +**Changed** + +- **BREAKING CHANGE:** Polish `ShadowSpec`. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) + - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. + - `ShadowSpec` no longer extends `CopySpec`. + - Overload `relocate`, `transform` and things for better usability in Kotlin. + ## [v9.0.0-beta10] (2025-03-05) diff --git a/lint-baseline.xml b/lint-baseline.xml index 7ab8f759e..1aeb46647 100644 --- a/lint-baseline.xml +++ b/lint-baseline.xml @@ -1,5 +1,5 @@ - + = super.getExcludes() - override fun minimize(): ShadowJar = apply { + override fun minimize() { minimizeJar.set(true) } - override fun minimize(action: Action?): ShadowJar = apply { + override fun minimize(action: Action?) { minimize() action?.execute(dependencyFilterForMinimize) } - override fun dependencies(action: Action?): ShadowJar = apply { + override fun dependencies(action: Action?) { action?.execute(dependencyFilter.get()) } - override fun transform(clazz: Class): ShadowJar { - return transform(clazz, null) + override fun mergeServiceFiles() { + transform(ServiceFileTransformer::class.java, null) } - override fun transform(clazz: Class, action: Action?): ShadowJar = apply { - addTransform(clazz.create(objectFactory), action) - } - - override fun transform(transformer: ResourceTransformer): ShadowJar = apply { - addTransform(transformer, null) - } - - override fun mergeServiceFiles(): ShadowJar { - return runCatching { - transform(ServiceFileTransformer::class.java, null) - }.getOrDefault(this) - } - - override fun mergeServiceFiles(rootPath: String): ShadowJar { - return runCatching { - transform(ServiceFileTransformer::class.java) { - it.path = rootPath - } - }.getOrDefault(this) - } - - override fun mergeServiceFiles(action: Action?): ShadowJar { - return runCatching { - transform(ServiceFileTransformer::class.java, action) - }.getOrDefault(this) + override fun mergeServiceFiles(rootPath: String) { + transform(ServiceFileTransformer::class.java) { + it.path = rootPath + } } - override fun mergeGroovyExtensionModules(): ShadowJar { - return runCatching { - transform(GroovyExtensionModuleTransformer::class.java, null) - }.getOrDefault(this) + override fun mergeServiceFiles(action: Action?) { + transform(ServiceFileTransformer::class.java, action) } - override fun append(resourcePath: String): ShadowJar { - return append(resourcePath, AppendingTransformer.DEFAULT_SEPARATOR) + override fun mergeGroovyExtensionModules() { + transform(GroovyExtensionModuleTransformer::class.java, null) } /** @@ -210,39 +186,53 @@ public abstract class ShadowJar : * @param separator The separator to use between the original content and the appended content, * defaults to `\n` ([AppendingTransformer.DEFAULT_SEPARATOR]). */ - override fun append(resourcePath: String, separator: String): ShadowJar { - return runCatching { - transform(AppendingTransformer::class.java) { - it.resource.set(resourcePath) - it.separator.set(separator) - } - }.getOrDefault(this) - } - - override fun relocate(pattern: String, destination: String): ShadowJar { - return relocate(pattern, destination, null) + override fun append(resourcePath: String, separator: String) { + transform(AppendingTransformer::class.java) { + it.resource.set(resourcePath) + it.separator.set(separator) + } } override fun relocate( pattern: String, destination: String, action: Action?, - ): ShadowJar = apply { + ) { val relocator = SimpleRelocator(pattern, destination) addRelocator(relocator, action) } - override fun relocate(relocator: Relocator): ShadowJar = apply { - addRelocator(relocator, null) + override fun relocate(clazz: Class, action: Action?) { + val relocator = clazz.getDeclaredConstructor().newInstance() + addRelocator(relocator, action) } - override fun relocate(clazz: Class): ShadowJar { - return relocate(clazz, null) + override fun relocate(relocator: R, action: Action?) { + addRelocator(relocator, action) } - override fun relocate(clazz: Class, action: Action?): ShadowJar = apply { - val relocator = clazz.getDeclaredConstructor().newInstance() - addRelocator(relocator, action) + public inline fun relocate() { + relocate(R::class.java, null) + } + + public inline fun relocate(action: Action?) { + relocate(R::class.java, action) + } + + override fun transform(clazz: Class, action: Action?) { + addTransform(clazz.create(objectFactory), action) + } + + override fun transform(transformer: T, action: Action?) { + addTransform(transformer, action) + } + + public inline fun transform() { + transform(T::class.java, null) + } + + public inline fun transform(action: Action?) { + transform(T::class.java, action) } @TaskAction diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt index 8d631fd65..f0a01509c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -2,68 +2,60 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator +import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import java.lang.reflect.InvocationTargetException import org.gradle.api.Action -import org.gradle.api.file.CopySpec -public interface ShadowSpec : CopySpec { - public fun minimize(): ShadowSpec +@JvmDefaultWithCompatibility +public interface ShadowSpec { + public fun minimize() - public fun minimize(action: Action?): ShadowSpec + public fun minimize(action: Action?) - public fun dependencies(action: Action?): ShadowSpec + public fun dependencies(action: Action?) - @Throws( - InstantiationException::class, - IllegalAccessException::class, - NoSuchMethodException::class, - InvocationTargetException::class, - ) - public fun transform(clazz: Class): ShadowSpec + public fun mergeServiceFiles() - @Throws( - InstantiationException::class, - IllegalAccessException::class, - NoSuchMethodException::class, - InvocationTargetException::class, - ) - public fun transform(clazz: Class, action: Action?): ShadowSpec + public fun mergeServiceFiles(rootPath: String) - public fun transform(transformer: ResourceTransformer): ShadowSpec + public fun mergeServiceFiles(action: Action?) - public fun mergeServiceFiles(): ShadowSpec + public fun mergeGroovyExtensionModules() - public fun mergeServiceFiles(rootPath: String): ShadowSpec + public fun append(resourcePath: String) { + append(resourcePath, AppendingTransformer.DEFAULT_SEPARATOR) + } - public fun mergeServiceFiles(action: Action?): ShadowSpec + public fun append(resourcePath: String, separator: String) - public fun mergeGroovyExtensionModules(): ShadowSpec + public fun relocate(pattern: String, destination: String) { + relocate(pattern, destination, null) + } - public fun append(resourcePath: String): ShadowSpec + public fun relocate(pattern: String, destination: String, action: Action?) - public fun append(resourcePath: String, separator: String): ShadowSpec + public fun relocate(clazz: Class) { + relocate(clazz, null) + } - public fun relocate(pattern: String, destination: String): ShadowSpec + public fun relocate(clazz: Class, action: Action?) - public fun relocate(pattern: String, destination: String, action: Action?): ShadowSpec + public fun relocate(relocator: R) { + relocate(relocator, null) + } - public fun relocate(relocator: Relocator): ShadowSpec + public fun relocate(relocator: R, action: Action?) - @Throws( - InstantiationException::class, - IllegalAccessException::class, - NoSuchMethodException::class, - InvocationTargetException::class, - ) - public fun relocate(clazz: Class): ShadowSpec + public fun transform(clazz: Class) { + transform(clazz, null) + } - @Throws( - InstantiationException::class, - IllegalAccessException::class, - NoSuchMethodException::class, - InvocationTargetException::class, - ) - public fun relocate(clazz: Class, action: Action?): ShadowSpec + public fun transform(clazz: Class, action: Action?) + + public fun transform(transformer: T) { + transform(transformer, null) + } + + public fun transform(transformer: T, action: Action?) } From 379c64508a9a2bd791b34e3996a72f31feb19b8f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 5 Mar 2025 08:43:09 -0500 Subject: [PATCH 305/941] Remove redundant types from function returns (#1308) We don't call them as "builders". Follow up c8b6991ec17799a5a0b8004e0122775e04dc0b18. --- api/shadow.api | 20 +++++++++++-------- docs/changes/README.md | 1 + .../internal/AbstractDependencyFilter.kt | 4 ++-- .../shadow/internal/DefaultInheritManifest.kt | 8 +------- .../shadow/relocation/SimpleRelocator.kt | 4 ++-- .../plugins/shadow/tasks/DependencyFilter.kt | 4 ++-- .../plugins/shadow/tasks/InheritManifest.kt | 7 +++++-- .../ManifestAppenderTransformer.kt | 2 +- .../ManifestResourceTransformer.kt | 2 +- 9 files changed, 27 insertions(+), 25 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index e513aa7fc..bc41314e7 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -134,11 +134,11 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public fun canRelocateClass (Ljava/lang/String;)Z public fun canRelocatePath (Ljava/lang/String;)Z public fun equals (Ljava/lang/Object;)Z - public fun exclude (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; + public fun exclude (Ljava/lang/String;)V public final fun getExcludes ()Ljava/util/Set; public final fun getIncludes ()Ljava/util/Set; public fun hashCode ()I - public fun include (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator; + public fun include (Ljava/lang/String;)V public fun relocateClass-XBGRxQs (Ljava/lang/String;)Ljava/lang/String; public fun relocatePath-bvWaKNU (Ljava/lang/String;)Ljava/lang/String; } @@ -146,8 +146,8 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter : java/io/Serializable { public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; - public abstract fun exclude (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter; - public abstract fun include (Lorg/gradle/api/specs/Spec;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter; + public abstract fun exclude (Lorg/gradle/api/specs/Spec;)V + public abstract fun include (Lorg/gradle/api/specs/Spec;)V public abstract fun project (Ljava/lang/String;)Lorg/gradle/api/specs/Spec; public abstract fun project (Ljava/util/Map;)Lorg/gradle/api/specs/Spec; public abstract fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; @@ -155,8 +155,12 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks } public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest : org/gradle/api/java/archives/Manifest { - public abstract fun inheritFrom ([Ljava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; - public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; + public fun inheritFrom ([Ljava/lang/Object;)V + public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)V +} + +public final class com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest$DefaultImpls { + public static fun inheritFrom (Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest;[Ljava/lang/Object;)V } public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { @@ -345,7 +349,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2Plugi public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V - public fun append (Ljava/lang/String;Ljava/lang/Comparable;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer; + public fun append (Ljava/lang/String;Ljava/lang/Comparable;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getAttributes ()Lorg/gradle/api/provider/SetProperty; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; @@ -356,7 +360,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestApp public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V - public fun attributes (Ljava/util/Map;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer; + public fun attributes (Ljava/util/Map;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getMainClass ()Lorg/gradle/api/provider/Property; public fun getManifestEntries ()Lorg/gradle/api/provider/MapProperty; diff --git a/docs/changes/README.md b/docs/changes/README.md index 1de957574..9af5785bc 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -9,6 +9,7 @@ - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. - `ShadowSpec` no longer extends `CopySpec`. - Overload `relocate`, `transform` and things for better usability in Kotlin. +- **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) ## [v9.0.0-beta10] (2025-03-05) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt index 06f905d56..6709c625c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -35,11 +35,11 @@ internal sealed class AbstractDependencyFilter( ?: project.files() } - override fun exclude(spec: Spec): DependencyFilter = apply { + override fun exclude(spec: Spec) { excludeSpecs.add(spec) } - override fun include(spec: Spec): DependencyFilter = apply { + override fun include(spec: Spec) { includeSpecs.add(spec) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt index 3a3e21254..5de7d0381 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt @@ -14,16 +14,10 @@ internal class DefaultInheritManifest @JvmOverloads constructor( Manifest by internalManifest { private val inheritMergeSpecs = mutableListOf() - override fun inheritFrom( - vararg inheritPaths: Any, - ): InheritManifest { - return inheritFrom(inheritPaths = inheritPaths, action = null) - } - override fun inheritFrom( vararg inheritPaths: Any, action: Action<*>?, - ): InheritManifest = apply { + ) { val mergeSpec = DefaultManifestMergeSpec() mergeSpec.from(*inheritPaths) inheritMergeSpecs.add(mergeSpec) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index f8e39c9ea..9ca8297ae 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -84,11 +84,11 @@ public open class SimpleRelocator @JvmOverloads constructor( } } - public open fun include(pattern: String): SimpleRelocator = apply { + public open fun include(pattern: String) { includes.addAll(normalizePatterns(listOf(pattern))) } - public open fun exclude(pattern: String): SimpleRelocator = apply { + public open fun exclude(pattern: String) { excludes.addAll(normalizePatterns(listOf(pattern))) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index f12ada8ea..90d3a2d82 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -22,12 +22,12 @@ public interface DependencyFilter : Serializable { /** * Exclude dependencies that match the provided [spec]. */ - public fun exclude(spec: Spec): DependencyFilter + public fun exclude(spec: Spec) /** * Include dependencies that match the provided [spec]. */ - public fun include(spec: Spec): DependencyFilter + public fun include(spec: Spec) /** * Create a [Spec] that matches the provided project [notation]. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt index d370b5928..93bf5984f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt @@ -3,8 +3,11 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import org.gradle.api.Action import org.gradle.api.java.archives.Manifest +@JvmDefaultWithCompatibility public interface InheritManifest : Manifest { - public fun inheritFrom(vararg inheritPaths: Any): InheritManifest + public fun inheritFrom(vararg inheritPaths: Any) { + inheritFrom(inheritPaths = inheritPaths, action = null) + } - public fun inheritFrom(vararg inheritPaths: Any, action: Action<*>?): InheritManifest + public fun inheritFrom(vararg inheritPaths: Any, action: Action<*>?) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 5643d0817..ca6f75b03 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -64,7 +64,7 @@ public open class ManifestAppenderTransformer @Inject constructor( } } - public open fun append(name: String, value: Comparable<*>): ManifestAppenderTransformer = apply { + public open fun append(name: String, value: Comparable<*>) { attributes.add(Pair(name, value)) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 6a848f2b5..7165f95d7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -81,7 +81,7 @@ public open class ManifestResourceTransformer @Inject constructor( manifest!!.write(os) } - public open fun attributes(attributes: Map): ManifestResourceTransformer = apply { + public open fun attributes(attributes: Map) { manifestEntries.putAll(attributes) } From 92486cb571c8f67629dff1d68e94c56ea1714a63 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 5 Mar 2025 09:31:14 -0500 Subject: [PATCH 306/941] Update Groovy code block styles (#1309) --- docs/application-plugin/README.md | 99 +++---- docs/configuration/README.md | 75 +++-- docs/configuration/dependencies/README.md | 161 +++++------ docs/configuration/filtering/README.md | 23 +- docs/configuration/merging/README.md | 264 ++++++++---------- docs/configuration/minimizing/README.md | 34 +-- docs/configuration/relocation/README.md | 52 ++-- .../reproducible-builds/README.md | 10 +- docs/custom-tasks/README.md | 41 ++- docs/getting-started/README.md | 64 ++--- docs/kmp-plugin/README.md | 52 ++-- docs/multi-project/README.md | 9 +- docs/plugins/README.md | 38 ++- docs/publishing/README.md | 162 +++++------ 14 files changed, 477 insertions(+), 607 deletions(-) diff --git a/docs/application-plugin/README.md b/docs/application-plugin/README.md index a04f90c0f..bb4db86b9 100644 --- a/docs/application-plugin/README.md +++ b/docs/application-plugin/README.md @@ -7,18 +7,15 @@ configure additional tasks for running the shadowed JAR and creating distributio Just like the normal `jar` task, when the `application` plugin is applied, the `shadowJar` manifest will be configured to contain the `Main-Class` attribute with the value specified in the project's `mainClassName` attribute. -```groovy -// Using Shadow with Application Plugin -plugins { - id 'java' - id 'application' - id 'com.gradleup.shadow' -} - -application { - mainClass = 'myapp.Main' -} -``` + plugins { + id 'java' + id 'application' + id 'com.gradleup.shadow' + } + + application { + mainClass = 'myapp.Main' + } ## Running the Shadow JAR @@ -28,24 +25,21 @@ The `runShadow` task is a [`JavaExec`](https://docs.gradle.org/current/dsl/org.g task that is configured to execute `java -jar myproject-all.jar`. It can be configured the same as any other `JavaExec` task. -```groovy -// Configuring the runShadow Task -plugins { - id 'java' - id 'application' - id 'com.gradleup.shadow' -} - -application { - mainClass = 'myapp.Main' - // Optionally, you can add default JVM arguments to the start scripts like this: - applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] -} - -tasks.named('runShadow', JavaExec) { - args 'foo' -} -``` + plugins { + id 'java' + id 'application' + id 'com.gradleup.shadow' + } + + application { + mainClass = 'myapp.Main' + // Optionally, you can add default JVM arguments to the start scripts like this: + applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] + } + + tasks.named('runShadow', JavaExec) { + args 'foo' + } ## Distributing the Shadow JAR @@ -60,30 +54,27 @@ files for a distribution to `build/install/-shadow/`. You can also add more files into the distribution like: -```groovy -// Add extra files to the distribution -plugins { - id 'java' - id 'application' - id 'com.gradleup.shadow' -} - -application { - mainClass = 'myapp.Main' - // Optionally, you can include `some/dir` files in the distribution like this: - applicationDistribution.from('some/dir') { - include '*.txt' - } -} - -// `shadow` is the name of the distribution created by Shadow plugin -distributions.named('shadow') { - // Optionally, you can add more files into extra directory in the distribution like this: - contents.from('extra/echo.sh') { - into 'extra' - } -} -``` + plugins { + id 'java' + id 'application' + id 'com.gradleup.shadow' + } + + application { + mainClass = 'myapp.Main' + // Optionally, you can include `some/dir` files in the distribution like this: + applicationDistribution.from('some/dir') { + include '*.txt' + } + } + + // `shadow` is the name of the distribution created by Shadow plugin + distributions.named('shadow') { + // Optionally, you can add more files into extra directory in the distribution like this: + contents.from('extra/echo.sh') { + into 'extra' + } + } View [the official doc described](https://docs.gradle.org/current/userguide/distribution_plugin.html#distribution_plugin) for more information about configuring distributions. diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 41c2d0db0..5619d73f6 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -22,14 +22,11 @@ file at: `build/libs/myApp-1.0-all.jar` As with all `Jar` tasks in Gradle, these values can be overridden: -```groovy -// Output to build/libs/shadow.jar -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - archiveBaseName = 'shadow' - archiveClassifier = '' - archiveVersion = '' -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + archiveBaseName = 'shadow' + archiveClassifier = '' + archiveVersion = '' + } ## Configuring the Runtime Classpath @@ -50,11 +47,9 @@ in the JAR manifest. The value of the `Class-Path` entry is the name of all dependencies resolved in the `shadow` configuration for the project. -```groovy -dependencies { - shadow 'junit:junit:3.8.2' -} -``` + dependencies { + shadow 'junit:junit:3.8.2' + } Inspecting the `META-INF/MANIFEST.MF` entry in the JAR file will reveal the following attribute: @@ -71,13 +66,11 @@ Beyond the automatic configuration of the `Class-Path` entry, the `shadowJar` ma First, the manifest for the `shadowJar` task is configured to __inherit__ from the manifest of the standard `jar` task. This means that any configuration performed on the `jar` task will propagate to the `shadowJar` tasks. -```groovy -tasks.named('jar', Jar) { - manifest { - attributes 'Class-Path': '/libs/a.jar' - } -} -``` + tasks.named('jar', Jar) { + manifest { + attributes 'Class-Path': '/libs/a.jar' + } + } Inspecting the `META-INF/MANIFEST.MF` entry in the JAR file will reveal the following attribute: @@ -88,17 +81,15 @@ Class-Path: /libs/a.jar If it is desired to inherit a manifest from a JAR task other than the standard `jar` task, the `inheritFrom` methods on the `shadowJar.manifest` object can be used to configure the upstream. -```groovy -def testJar = tasks.register('testJar', Jar) { - manifest { - attributes 'Description': 'This is an application JAR' - } -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - manifest.inheritFrom(testJar.get().manifest) -} -``` + def testJar = tasks.register('testJar', Jar) { + manifest { + attributes 'Description': 'This is an application JAR' + } + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + manifest.inheritFrom(testJar.get().manifest) + } ## Adding Extra Files @@ -106,15 +97,13 @@ The `shadowJar` task is a subclass of the `Jar` task, which means that the [from](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20groovy.lang.Closure)) method can be used to add extra files. -```groovy -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - from('extra.jar') { - // Copy extra.jar file (without unzipping) into META-INF/ in the shadowed JAR. - into('META-INF') - } - from('Foo') { - // Copy Foo file into Bar/ in the shadowed JAR. - into('Bar') - } -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + from('extra.jar') { + // Copy extra.jar file (without unzipping) into META-INF/ in the shadowed JAR. + into('META-INF') + } + from('Foo') { + // Copy Foo file into Bar/ in the shadowed JAR. + into('Bar') + } + } diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index fa3fcf6e8..e345bd35d 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -5,11 +5,9 @@ into the final JAR. The configurations from which to source dependencies for the merging can be configured using the `configurations` property of the [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type. -```groovy -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - configurations = [project.configurations.compileClasspath] -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + configurations = [project.configurations.compileClasspath] + } The above code sample would configure the `shadowJar` task to merge dependencies from only the `compileClasspath` configuration. This means any dependency declared in the `runtimeOnly` configuration would be **not** be included in the final JAR. @@ -38,31 +36,25 @@ That is, excluding a dependency does not exclude any of its dependencies from th The `dependency` blocks provides a number of methods for resolving dependencies using the notations familiar from Gradle's `configurations` block. -```groovy -// Exclude an Module Dependency -dependencies { - implementation 'org.apache.logging.log4j:log4j-core:2.11.1' -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - dependencies { - exclude(dependency('org.apache.logging.log4j:log4j-core:2.11.1')) - } -} -``` - -```groovy -// Exclude a Project Dependency -dependencies { - implementation project(':api') -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - dependencies { - exclude(project(':api')) - } -} -``` + dependencies { + implementation 'org.apache.logging.log4j:log4j-core:2.11.1' + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + dependencies { + exclude(dependency('org.apache.logging.log4j:log4j-core:2.11.1')) + } + } + + dependencies { + implementation project(':api') + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + dependencies { + exclude(project(':api')) + } + } > While not being able to filter entire transitive dependency graphs might seem like an oversight, it is necessary because it would not be possible to intelligently determine the build author's intended results when there is a @@ -74,63 +66,51 @@ Dependencies can be filtered using regex patterns. Coupled with the `::` notation for dependencies, this allows for excluding/including using any of these individual fields. -```groovy -// Exclude Any Version of a Dependency -dependencies { - implementation 'org.apache.logging.log4j:log4j-core:2.11.1' -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - dependencies { - exclude(dependency('org.apache.logging.log4j:log4j-core:.*')) - } -} -``` + dependencies { + implementation 'org.apache.logging.log4j:log4j-core:2.11.1' + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + dependencies { + exclude(dependency('org.apache.logging.log4j:log4j-core:.*')) + } + } Any of the individual fields can be safely absent and will function as though a wildcard was specified. -```groovy -// Ignore Dependency Version -dependencies { - implementation 'org.apache.logging.log4j:log4j-core:2.11.1' -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - dependencies { - exclude(dependency('org.apache.logging.log4j:log4j-core')) - } -} -``` + dependencies { + implementation 'org.apache.logging.log4j:log4j-core:2.11.1' + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + dependencies { + exclude(dependency('org.apache.logging.log4j:log4j-core')) + } + } The above code snippet is functionally equivalent to the previous example. This same pattern can be used for any of the dependency notation fields. -```groovy -// Ignoring An Artifact Regardless of Group -dependencies { - implementation 'org.apache.logging.log4j:log4j-core:2.11.1' -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - dependencies { - exclude(dependency(':log4j-core:2.11.1')) - } -} -``` - -```groovy -// Excluding All Artifacts From Group -dependencies { - implementation 'org.apache.logging.log4j:log4j-core:2.11.1' -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - dependencies { - exclude(dependency('org.apache.logging.log4j:2.11.1')) - } -} -``` + dependencies { + implementation 'org.apache.logging.log4j:log4j-core:2.11.1' + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + dependencies { + exclude(dependency(':log4j-core:2.11.1')) + } + } + + dependencies { + implementation 'org.apache.logging.log4j:log4j-core:2.11.1' + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + dependencies { + exclude(dependency('org.apache.logging.log4j:2.11.1')) + } + } ### Programmatically Selecting Dependencies to Filter @@ -138,17 +118,14 @@ If more complex decisions are needed to select the dependencies to be included, [`dependencies`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html) block provides a method that accepts a `Closure` for selecting dependencies. -```groovy -// Selecting Dependencies to Filter With a Spec -dependencies { - implementation 'org.apache.logging.log4j:log4j-core:2.11.1' -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - dependencies { - exclude { - it.moduleGroup == 'org.apache.logging.log4j' + dependencies { + implementation 'org.apache.logging.log4j:log4j-core:2.11.1' + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + dependencies { + exclude { + it.moduleGroup == 'org.apache.logging.log4j' + } + } } - } -} -``` diff --git a/docs/configuration/filtering/README.md b/docs/configuration/filtering/README.md index d7a2ee3bd..93aa6e880 100644 --- a/docs/configuration/filtering/README.md +++ b/docs/configuration/filtering/README.md @@ -11,22 +11,15 @@ contents. This means that, the configuration is applied to the individual files from both the project source set or _any_ of the dependencies to be merged. -```groovy -// Exclude a file from Shadow Jar -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - exclude 'a2.properties' -} -``` - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + exclude 'a2.properties' + } Excludes and includes can be combined just like a normal `Jar` task, with `excludes` taking precedence over `includes`. Additionally, ANT style patterns can be used to match multiple files. -```groovy -// Configuring output using ANT patterns -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - include '*.jar' - include '*.properties' - exclude 'a2.properties' -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + include '*.jar' + include '*.properties' + exclude 'a2.properties' + } diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 9e6c69b85..45bedef0d 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -8,100 +8,91 @@ entry in the JAR before being written to the final output JAR. This allows a [`Transformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-transformer/index.html) to determine if it should process a particular entry and apply any modifications before writing the stream to the output. -```groovy -// Adding a ResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext -import javax.annotation.Nonnull -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement - -class MyTransformer implements ResourceTransformer { - @Override - boolean canTransformResource(@Nonnull FileTreeElement element) { return true } - - @Override - void transform(@Nonnull TransformerContext context) {} - - @Override - boolean hasTransformedResource() { return true } - - @Override - void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(MyTransformer.class) -} -``` + import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer + import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext + import javax.annotation.Nonnull + import org.apache.tools.zip.ZipOutputStream + import org.gradle.api.file.FileTreeElement + + class MyTransformer implements ResourceTransformer { + @Override + boolean canTransformResource(@Nonnull FileTreeElement element) { return true } + + @Override + void transform(@Nonnull TransformerContext context) {} + + @Override + boolean hasTransformedResource() { return true } + + @Override + void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + transform(MyTransformer.class) + } Additionally, a `Transformer` can accept a `Closure` to configure the provided `Transformer`. -```groovy -// Configuring a ResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext -import javax.annotation.Nonnull -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement - -class MyTransformer implements ResourceTransformer { - boolean enabled - - @Override - boolean canTransformResource(@Nonnull FileTreeElement element) { return true } - - @Override - void transform(@Nonnull TransformerContext context) {} - - @Override - boolean hasTransformedResource() { return true } - - @Override - void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(MyTransformer.class) { - enabled = true - } -} -``` + import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer + import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext + import javax.annotation.Nonnull + import org.apache.tools.zip.ZipOutputStream + import org.gradle.api.file.FileTreeElement + + class MyTransformer implements ResourceTransformer { + boolean enabled + + @Override + boolean canTransformResource(@Nonnull FileTreeElement element) { return true } + + @Override + void transform(@Nonnull TransformerContext context) {} + + @Override + boolean hasTransformedResource() { return true } + + @Override + void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + transform(MyTransformer.class) { + enabled = true + } + } An instantiated instance of a `Transformer` can also be provided. -```groovy -// Adding a ResourceTransformer Instance -import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext -import javax.annotation.Nonnull -import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement - -class MyTransformer implements ResourceTransformer { - final boolean enabled - - MyTransformer(boolean enabled) { - this.enabled = enabled - } - - @Override - boolean canTransformResource(@Nonnull FileTreeElement element) { return true } - - @Override - void transform(@Nonnull TransformerContext context) {} - - @Override - boolean hasTransformedResource() { return true } - - @Override - void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(new MyTransformer(true)) -} -``` + import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer + import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext + import javax.annotation.Nonnull + import org.apache.tools.zip.ZipOutputStream + import org.gradle.api.file.FileTreeElement + + class MyTransformer implements ResourceTransformer { + final boolean enabled + + MyTransformer(boolean enabled) { + this.enabled = enabled + } + + @Override + boolean canTransformResource(@Nonnull FileTreeElement element) { return true } + + @Override + void transform(@Nonnull TransformerContext context) {} + + @Override + boolean hasTransformedResource() { return true } + + @Override + void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + transform(new MyTransformer(true)) + } ## Merging Service Descriptor Files @@ -115,12 +106,9 @@ The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github class is used to perform this merging. By default, it will merge each copy of a file under `META-INF/services` into a single file in the output JAR. -```groovy -// Merging Service Files -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - mergeServiceFiles() -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + mergeServiceFiles() + } The above code snippet is a convenience syntax for calling [`transform(ServiceFileTransformer.class)`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html). @@ -137,28 +125,22 @@ By default the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow is configured to merge files in `META-INF/services`. This directory can be overridden to merge descriptor files in a different location. -```groovy -// Merging Service Files in a Specific Directory -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - mergeServiceFiles { - path = 'META-INF/custom' - } -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + mergeServiceFiles { + path = 'META-INF/custom' + } + } #### Excluding/Including Specific Service Descriptor Files From Merging The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) class supports specifying specific files to include or exclude from merging. -```groovy -// Excluding a Service Descriptor From Merging -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - mergeServiceFiles { - exclude 'META-INF/services/com.acme.*' - } -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + mergeServiceFiles { + exclude 'META-INF/services/com.acme.*' + } + } ## Merging Groovy Extension Modules @@ -169,24 +151,18 @@ will handle these files. The [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task also provides a short syntax method to add this transformer. -```groovy -// Merging Groovy Extension Modules -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - mergeGroovyExtensionModules() -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + mergeGroovyExtensionModules() + } ## Merging Log4j2 Plugin Cache Files (`Log4j2Plugins.dat`) `Log4j2PluginsCacheFileTransformer` is a `Transformer` that merges `META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat` plugin caches from all the jars containing Log4j 2.x Core components. It's a Gradle equivalent of [Log4j Plugin Descriptor Transformer](https://logging.apache.org/log4j/transform/log4j-transform-maven-shade-plugin-extensions.html#log4j-plugin-cache-transformer). -```groovy -// Merging Log4j2 Plugin Cache Files -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer.class) -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer.class) + } ## Appending Text Files @@ -198,26 +174,19 @@ method of [`append(String)`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html) to configure this transformer. -```groovy -// Appending a Property File -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - append 'test.properties' -} -``` - -```groovy -// Appending application.yml files -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - // short syntax - append('resources/application.yml', '\n---\n') - // full syntax - transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer.class) { - resource = 'resources/custom-config/application.yml' - separator = '\n---\n' - } -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + append 'test.properties' + } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // short syntax + append('resources/application.yml', '\n---\n') + // full syntax + transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer.class) { + resource = 'resources/custom-config/application.yml' + separator = '\n---\n' + } + } ## Appending XML Files @@ -227,11 +196,8 @@ reads each XML document and merges each root element into a single document. There is no short syntax method for the [`XmlAppendingTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html). It must be added using the [`transform`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html)) methods. -```groovy -// Appending a XML File -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer.class) { - resource = 'properties.xml' - } -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + transform(com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer.class) { + resource = 'properties.xml' + } + } diff --git a/docs/configuration/minimizing/README.md b/docs/configuration/minimizing/README.md index b00f514f6..2f9f84286 100644 --- a/docs/configuration/minimizing/README.md +++ b/docs/configuration/minimizing/README.md @@ -2,38 +2,30 @@ Shadow can automatically remove all classes of dependencies that are not used by the project, thereby minimizing the resulting shadowed JAR. -```groovy -// Minimizing a shadow JAR -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - minimize() -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + minimize() + } A dependency can be excluded from the minimization process, thereby forcing it's inclusion the shadow JAR. This is useful when the dependency analyzer cannot find the usage of a class programmatically, for example if the class is loaded dynamically via `Class.forName(String)`. Each of the `group`, `name` and `version` fields separated by `:` of a `dependency` is interpreted as a regular expression. -```groovy -// Force a class to be retained during minimization -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - minimize { - exclude(dependency('org.scala-lang:.*:.*')) - } -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + minimize { + exclude(dependency('org.scala-lang:.*:.*')) + } + } > Dependencies scoped as `api` will automatically excluded from minimization and used as "entry points" on minimization. Similar to dependencies, projects can also be excluded. -```groovy -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - minimize { - exclude(project(":api")) - } -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + minimize { + exclude(project(":api")) + } + } > When excluding a `project`, all dependencies of the excluded `project` are automatically excluded as well. diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 1a0e9dc29..8261706e7 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -10,12 +10,9 @@ Shadow uses the ASM library to modify class byte code to replace the package nam statements for a class. Any non-class files that are stored within a package structure are also relocated to the new location. -```groovy -// Relocating a Package -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - relocate 'junit.framework', 'shadow.junit' -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + relocate 'junit.framework', 'shadow.junit' + } The code snippet will rewrite the location for any class in the `junit.framework` to be `shadow.junit`. For example, the class `junit.framework.TestCase` becomes `shadow.junit.TestCase`. @@ -36,29 +33,23 @@ Specific classes or files can be `included`/`excluded` from the relocation opera [Ant Path Matcher](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html) syntax to specify matching path for your files and directories. -```groovy -// Configuring Filtering for Relocation -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - relocate('junit.textui', 'a') { - exclude 'junit.textui.TestRunner' - } - relocate('junit.framework', 'b') { - include 'junit.framework.Test*' - } -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + relocate('junit.textui', 'a') { + exclude 'junit.textui.TestRunner' + } + relocate('junit.framework', 'b') { + include 'junit.framework.Test*' + } + } For a more advanced path matching you might want to use [Regular Expressions](https://regexr.com/) instead. Wrap the expresion in `%regex[]` before passing it to `include`/`exclude`. -```groovy -// Configuring Filtering for Relocation with Regex -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - relocate('org.foo', 'a') { - include '%regex[org/foo/.*Factory[0-9].*]' - } -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + relocate('org.foo', 'a') { + include '%regex[org/foo/.*Factory[0-9].*]' + } + } ## Automatically Relocating Dependencies @@ -69,13 +60,10 @@ removed for clarity reasons in version 4.0.0. To configure automatic dependency relocation, set `enableRelocation = true` and optionally specify a custom `relocationPrefix` to override the default value of `"shadow"`. -```groovy -// Configure Auto Relocation -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation = true - relocationPrefix = "myapp" -} -``` + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + enableRelocation = true + relocationPrefix = "myapp" + } In versions before 8.1.0 it was necessary to configure a separate `ConfigureShadowRelocation` task for this. diff --git a/docs/configuration/reproducible-builds/README.md b/docs/configuration/reproducible-builds/README.md index aaec5858d..00bd16f14 100644 --- a/docs/configuration/reproducible-builds/README.md +++ b/docs/configuration/reproducible-builds/README.md @@ -2,12 +2,10 @@ By default, JAR files generated by Gradle (with or without Shadow) for a single project with the same source code may not be identical to each other. Sometimes it's desireable to configure a project to consistently output a byte-for-byte identical JAR on every build. Gradle supports this with the following configuration, and Shadow will correctly respect these settings too: -```groovy -tasks.withType(AbstractArchiveTask).configureEach { - preserveFileTimestamps = false - reproducibleFileOrder = true -} -``` + tasks.withType(AbstractArchiveTask).configureEach { + preserveFileTimestamps = false + reproducibleFileOrder = true + } One effect that this configuration will have is that the timestamps of all files in the JAR will be reset to a single consistent value. If your code or any files being included into the JAR depend on the timestamps being set accurately within the JAR, then this may not be the correct choice for you. diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index bbb7a0ebd..911df990c 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -5,28 +5,25 @@ It is possible to add arbitrary [`ShadowJar`](https://gradleup.com/shadow/api/sh tasks to a project. When doing so, ensure that the `configurations` property is specified to inform Shadow which dependencies to merge into the output. -```groovy -// Shadowing Test Sources and Dependencies -def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME - description = "Create a combined JAR of project and test dependencies" - - archiveClassifier = "tests" - from sourceSets.test.output - configurations = [project.configurations.testRuntimeClasspath] - - manifest { - // Optionally, set the main class for the JAR. - attributes 'Main-Class': 'test.Main' - // You can also set other attributes here. - } -} - -// Optionally, make the `assemble` task depend on the new task. -tasks.named('assemble') { - dependsOn testShadowJar -} -``` + def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + description = "Create a combined JAR of project and test dependencies" + + archiveClassifier = "tests" + from sourceSets.test.output + configurations = [project.configurations.testRuntimeClasspath] + + manifest { + // Optionally, set the main class for the JAR. + attributes 'Main-Class': 'test.Main' + // You can also set other attributes here. + } + } + + // Optionally, make the `assemble` task depend on the new task. + tasks.named('assemble') { + dependsOn testShadowJar + } The code snippet above will generate a shadowed JAR containing both the `main` and `test` sources as well as all `testRuntimeOnly` and `testImplementation` dependencies. diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 7067b632d..02ba9f034 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -1,28 +1,24 @@ # Getting Started -```groovy -plugins { - id 'java' - id 'com.gradleup.shadow' version '' -} -``` + plugins { + id 'java' + id 'com.gradleup.shadow' version '' + } Alternatively, the plugin can be added to the buildscript classpath and applied: -```groovy -buildscript { - repositories { - gradlePluginPortal() - } - dependencies { - classpath 'com.gradleup.shadow:shadow-gradle-plugin:' - } -} - -// `apply plugin` stuffs are used with `buildscript`. -apply plugin: 'java' -apply plugin: 'com.gradleup.shadow' -``` + buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + classpath 'com.gradleup.shadow:shadow-gradle-plugin:' + } + } + + // `apply plugin` stuffs are used with `buildscript`. + apply plugin: 'java' + apply plugin: 'com.gradleup.shadow'
Snapshots of the development version are available in @@ -31,21 +27,19 @@ Sonatype's snapshots repository.

-```groovy -buildscript { - repositories { - mavenCentral() - maven { url = 'https://oss.sonatype.org/content/repositories/snapshots/' } - } - dependencies { - classpath 'com.gradleup.shadow:shadow-gradle-plugin:' - } -} - -// `apply plugin` stuffs are used with `buildscript`. -apply plugin: 'java' -apply plugin: 'com.gradleup.shadow' -``` + buildscript { + repositories { + mavenCentral() + maven { url = 'https://oss.sonatype.org/content/repositories/snapshots/' } + } + dependencies { + classpath 'com.gradleup.shadow:shadow-gradle-plugin:' + } + } + + // `apply plugin` stuffs are used with `buildscript`. + apply plugin: 'java' + apply plugin: 'com.gradleup.shadow'

diff --git a/docs/kmp-plugin/README.md b/docs/kmp-plugin/README.md index 57dee7947..33b551801 100644 --- a/docs/kmp-plugin/README.md +++ b/docs/kmp-plugin/README.md @@ -3,35 +3,33 @@ 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 its `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" + 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" + } + } } } - 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' } } - } -} - -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' - } -} -``` diff --git a/docs/multi-project/README.md b/docs/multi-project/README.md index 62a8efcc6..aa412bed8 100644 --- a/docs/multi-project/README.md +++ b/docs/multi-project/README.md @@ -12,9 +12,6 @@ requires the shadowed JAR as a dependency. In this case, use Gradle's normal dependency declaration mechanism to depend on the `shadow` configuration of the shadowed project. -```groovy -// Depending On Shadow Output of Project -dependencies { - implementation project(path: ':api', configuration: 'shadow') -} -``` \ No newline at end of file + dependencies { + implementation project(path: ':api', configuration: 'shadow') + } diff --git a/docs/plugins/README.md b/docs/plugins/README.md index afc771dc5..3dde6678e 100644 --- a/docs/plugins/README.md +++ b/docs/plugins/README.md @@ -10,26 +10,24 @@ and `relocationPrefix` settings on any `ShadowJar` task. A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the `shadowJar` task for relocation. -```groovy -plugins { - id 'java-gradle-plugin' // May have to apply the latest `com.gradle.plugin-publish` for better publishing support. - id 'com.gradleup.shadow' -} - -dependencies { - implementation 'org.jdom:jdom2:2.0.6' - implementation 'org.ow2.asm:asm:6.0' - implementation 'org.ow2.asm:asm-commons:6.0' - implementation 'commons-io:commons-io:2.4' - implementation 'org.apache.ant:ant:1.9.4' - implementation 'org.codehaus.plexus:plexus-utils:2.0.6' -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation = true - archiveClassifier = '' -} -``` + plugins { + id 'java-gradle-plugin' // May have to apply the latest `com.gradle.plugin-publish` for better publishing support. + id 'com.gradleup.shadow' + } + + dependencies { + implementation 'org.jdom:jdom2:2.0.6' + implementation 'org.ow2.asm:asm:6.0' + implementation 'org.ow2.asm:asm-commons:6.0' + implementation 'commons-io:commons-io:2.4' + implementation 'org.apache.ant:ant:1.9.4' + implementation 'org.codehaus.plexus:plexus-utils:2.0.6' + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + enableRelocation = true + archiveClassifier = '' + } ## Publishing shadowed Gradle plugins The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 53b2de5fa..b32cf1985 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -7,27 +7,24 @@ The Shadow plugin will automatically configure the necessary tasks in the presen The plugin provides the `shadow` component to configure the publication with the necessary artifact and dependencies in the POM file. -```groovy -// Publishing a Shadow JAR with the Maven-Publish Plugin -plugins { - id 'java' - id 'maven-publish' - id 'com.gradleup.shadow' -} - -publishing { - publications { - shadow(MavenPublication) { - from(components.shadow) // or components["shadow"] in Kotlin DSL + plugins { + id 'java' + id 'maven-publish' + id 'com.gradleup.shadow' } - } - repositories { - maven { - url = "https://repo.myorg.com" + + publishing { + publications { + shadow(MavenPublication) { + from(components.shadow) // or components["shadow"] in Kotlin DSL + } + } + repositories { + maven { + url = "https://repo.myorg.com" + } + } } - } -} -``` ## Shadow Configuration and Publishing @@ -53,79 +50,74 @@ be manually configured. You may want to publish the shadowed JAR instead of the original JAR. This can be done by trimming the `archiveClassifier` of the shadowed JAR like the following: -```groovy -plugins { - id 'java' - id 'maven-publish' - id 'com.gradleup.shadow' -} - -group = 'shadow' -version = '1.0' - -dependencies { - // This will be bundled in the shadowed JAR and not declared in the POM. - implementation 'some:a:1.0' - // This will be excluded from the shadowed JAR but declared as a runtime dependency in `META-INF/MANIFEST.MF` - // file's `Class-Path` entry, and also in the POM file. - shadow 'some:b:1.0' - // This will be excluded from the shadowed JAR and not declared in the POM or `META-INF/MANIFEST.MF`. - compileOnly 'some:c:1.0' -} - -tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - archiveClassifier = '' -} - -publishing { - publications { - shadow(MavenPublication) { - from components.shadow + plugins { + id 'java' + id 'maven-publish' + id 'com.gradleup.shadow' + } + + group = 'shadow' + version = '1.0' + + dependencies { + // This will be bundled in the shadowed JAR and not declared in the POM. + implementation 'some:a:1.0' + // This will be excluded from the shadowed JAR but declared as a runtime dependency in `META-INF/MANIFEST.MF` + // file's `Class-Path` entry, and also in the POM file. + shadow 'some:b:1.0' + // This will be excluded from the shadowed JAR and not declared in the POM or `META-INF/MANIFEST.MF`. + compileOnly 'some:c:1.0' } - } - repositories { - maven { - url = "https://repo.myorg.com" + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + archiveClassifier = '' + } + + publishing { + publications { + shadow(MavenPublication) { + from components.shadow + } + } + repositories { + maven { + url = "https://repo.myorg.com" + } + } } - } -} -``` ## Publish Custom ShadowJar Task Outputs It is possible to publish a custom `ShadowJar` task's output via the [`MavenPublication.artifact(java.lang.Object)`](https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html#org.gradle.api.publish.maven.MavenPublication:artifact(java.lang.Object)) method. -```groovy -// Publishing a Shadow JAR with the Maven-Publish Plugin -plugins { - id 'java' - id 'maven-publish' - id 'com.gradleup.shadow' -} - -def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME - description = "Create a combined JAR of project and test dependencies" - archiveClassifier = "tests" - from sourceSets.test.output - configurations = [project.configurations.testRuntimeClasspath] -} - -dependencies { - testImplementation 'junit:junit:3.8.2' -} - -publishing { - publications { - shadow(MavenPublication) { - artifact(testShadowJar) + plugins { + id 'java' + id 'maven-publish' + id 'com.gradleup.shadow' + } + + def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + description = "Create a combined JAR of project and test dependencies" + archiveClassifier = "tests" + from sourceSets.test.output + configurations = [project.configurations.testRuntimeClasspath] + } + + dependencies { + testImplementation 'junit:junit:3.8.2' } - } - repositories { - maven { - url = "https://repo.myorg.com" + + publishing { + publications { + shadow(MavenPublication) { + artifact(testShadowJar) + } + } + repositories { + maven { + url = "https://repo.myorg.com" + } + } } - } -} -``` From d68d4b59f73ffd01ee5236215055b101166098c4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Mar 2025 00:19:35 -0500 Subject: [PATCH 307/941] Simplify code snippets executor (#1310) * Override the code * Comments * Tweak the regex * Cleanups * Tweak CodeSnippetExtractor * Optimize matching logic --- .../plugins/shadow/DocCodeSnippetTest.kt | 39 +++---- .../executable/CodeSnippetExecutable.kt | 22 ---- .../shadow/executable/CodeSnippetExtractor.kt | 92 --------------- .../shadow/executor/GroovyBuildExecutor.kt | 71 ------------ .../plugins/shadow/executor/NoopExecutor.kt | 9 -- .../shadow/executor/SnippetExecutor.kt | 10 -- .../shadow/fixture/GroovyDslFixture.kt | 27 ----- .../plugins/shadow/fixture/SnippetFixture.kt | 5 - .../shadow/snippet/CodeSnippetExtractor.kt | 61 ++++++++++ .../gradle/plugins/shadow/snippet/DslLang.kt | 9 ++ .../shadow/snippet/GroovyBuildExecutable.kt | 19 ++++ .../shadow/snippet/KotlinBuildExecutable.kt | 22 ++++ .../shadow/snippet/SnippetExecutable.kt | 105 ++++++++++++++++++ 13 files changed, 236 insertions(+), 255 deletions(-) delete mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt delete mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt delete mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt delete mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt delete mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt delete mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt delete mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt create mode 100644 src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt index 68b2a0e93..1af8dc917 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt @@ -1,35 +1,36 @@ package com.github.jengelman.gradle.plugins.shadow -import com.github.jengelman.gradle.plugins.shadow.executable.CodeSnippetExtractor -import com.github.jengelman.gradle.plugins.shadow.executor.GroovyBuildExecutor -import com.github.jengelman.gradle.plugins.shadow.executor.NoopExecutor -import com.github.jengelman.gradle.plugins.shadow.fixture.GroovyDslFixture +import com.github.jengelman.gradle.plugins.shadow.snippet.CodeSnippetExtractor +import com.github.jengelman.gradle.plugins.shadow.snippet.DslLang import java.nio.file.Path -import kotlin.io.path.Path +import kotlin.io.path.createTempDirectory import org.junit.jupiter.api.DynamicTest import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.io.TempDir class DocCodeSnippetTest { + @OptIn(ExperimentalStdlibApi::class) @TestFactory fun provideDynamicTests(@TempDir root: Path): List { - return fixtures.flatMap { (selector, executor) -> - CodeSnippetExtractor.extract(root, docsDir, selector, executor) - }.map { - DynamicTest.dynamicTest(it.testName, it) + val langExecutables = DslLang.entries.map { executor -> + CodeSnippetExtractor.extract(executor) } - } - companion object { - private val fixtures = mapOf( - "groovy" to GroovyBuildExecutor( - GroovyDslFixture, - GroovyDslFixture.importsExtractor, - ), - "groovy no-run" to NoopExecutor, - ) + check(langExecutables.sumOf { it.size } > 0) { + "No code snippets found." + } + check(langExecutables.size == DslLang.entries.size) { + "We must provide build script snippets for all languages." + } + check(langExecutables.map { it.size }.distinct().size == 1) { + "All languages must have the same number of code snippets." + } - val docsDir: Path = Path(System.getProperty("DOCS_DIR")) + return langExecutables.flatten().map { + // Create a temporary directory for each test, root will be deleted after all tests are run. + it.tempDir = createTempDirectory(root) + DynamicTest.dynamicTest(it.displayName, it) + } } } diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt deleted file mode 100644 index d2b80e573..000000000 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExecutable.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.executable - -import com.github.jengelman.gradle.plugins.shadow.executor.SnippetExecutor -import java.nio.file.Path -import kotlin.io.path.createTempDirectory -import org.junit.jupiter.api.function.Executable - -class CodeSnippetExecutable( - private val root: Path, - private val snippet: String, - val testName: String, - private val executor: SnippetExecutor, - private val exceptionTransformer: (Throwable) -> Throwable, -) : Executable { - override fun execute() { - try { - executor.execute(createTempDirectory(root), snippet) - } catch (t: Throwable) { - throw exceptionTransformer(t) - } - } -} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt deleted file mode 100644 index 95c5f5369..000000000 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executable/CodeSnippetExtractor.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.executable - -import com.github.jengelman.gradle.plugins.shadow.DocCodeSnippetTest -import com.github.jengelman.gradle.plugins.shadow.executor.SnippetExecutor -import java.nio.file.Path -import java.util.regex.Pattern -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.name -import kotlin.io.path.pathString -import kotlin.io.path.readText -import kotlin.io.path.relativeTo -import kotlin.io.path.walk - -object CodeSnippetExtractor { - fun extract( - root: Path, - docRoot: Path, - cssClass: String, - executor: SnippetExecutor, - ): List { - val snippets = mutableListOf() - val snippetBlockPattern = Pattern.compile("(?ims)```$cssClass\n(.*?)\n```") - @OptIn(ExperimentalPathApi::class) - docRoot.walk() - .filter { it.name.endsWith(".md", ignoreCase = true) } - .forEach { - addSnippets(root, snippets, it, snippetBlockPattern, executor) - } - return snippets - } - - private fun addSnippets( - root: Path, - snippets: MutableList, - path: Path, - snippetBlockPattern: Pattern, - executor: SnippetExecutor, - ) { - val source = path.readText() - val relativeDocPath = path.relativeTo(DocCodeSnippetTest.docsDir).pathString - val snippetsByLine = findSnippetsByLine(source, snippetBlockPattern) - - snippetsByLine.forEach { (lineNumber, snippet) -> - snippets.add(createSnippet(root, relativeDocPath, path, lineNumber, snippet, executor)) - } - } - - private fun findSnippetBlocks(code: String, snippetTagPattern: Pattern) = buildList { - val matcher = snippetTagPattern.matcher(code) - while (matcher.find()) { - add(matcher.group(0)) - } - } - - private fun findSnippetsByLine(source: String, snippetTagPattern: Pattern): Map { - val snippetBlocks = findSnippetBlocks(source, snippetTagPattern) - val snippetBlocksByLine = mutableMapOf() - - var codeIndex = 0 - snippetBlocks.forEach { block -> - codeIndex = source.indexOf(block, codeIndex) - val lineNumber = source.substring(0, codeIndex).lines().size + 1 - snippetBlocksByLine[lineNumber] = extractSnippetFromBlock(block) - codeIndex += block.length - } - - return snippetBlocksByLine - } - - private fun extractSnippetFromBlock(tag: String): String { - return tag.substring(tag.indexOf("\n") + 1, tag.lastIndexOf("\n")) - } - - private fun createSnippet( - root: Path, - sourceClassName: String, - sourcePath: Path, - lineNumber: Int, - snippet: String, - executor: SnippetExecutor, - ): CodeSnippetExecutable { - return CodeSnippetExecutable( - root, - snippet, - "$sourceClassName:$lineNumber", - executor, - ) { - val message = "The error line in the doc is near ${sourcePath.toUri()}:$lineNumber" - RuntimeException(message, it) - } - } -} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt deleted file mode 100644 index d5958cd5b..000000000 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/GroovyBuildExecutor.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.executor - -import com.github.jengelman.gradle.plugins.shadow.fixture.SnippetFixture -import java.nio.file.Path -import kotlin.io.path.createDirectory -import kotlin.io.path.writeText -import org.gradle.testkit.runner.GradleRunner - -class GroovyBuildExecutor( - override val fixture: SnippetFixture, - private val importExtractor: (String) -> List, -) : SnippetExecutor { - - override fun execute(tempDir: Path, snippet: String) { - tempDir.resolve("settings.gradle").writeText( - """ - dependencyResolutionManagement { - repositories { - mavenLocal() - mavenCentral() - } - } - include 'api', 'main' - """.trimIndent(), - ) - - val apiScript = """ - plugins { - id 'java' - id 'com.gradleup.shadow' - } - """.trimIndent() - tempDir.addSubProject("api", apiScript) - - val (imports, snippetWithoutImports) = importExtractor(snippet) - val mainScript = buildString { - append(imports) - append(System.lineSeparator()) - // All buildscript {} blocks must appear before any plugins {} blocks in the script. - if (snippetWithoutImports.contains("buildscript {")) { - append(snippetWithoutImports) - } else { - if (!snippetWithoutImports.contains("plugins {")) { - append(fixture.pluginsBlock) - append(System.lineSeparator()) - } - append(snippetWithoutImports) - } - append(System.lineSeparator()) - }.trimIndent() - tempDir.addSubProject("main", mainScript) - - try { - GradleRunner.create() - .withProjectDir(tempDir.toFile()) - .withPluginClasspath() - .forwardOutput() - .withArguments("--warning-mode=fail", "build") - .build() - } catch (t: Throwable) { - throw RuntimeException("Failed to execute snippet:\n\n$mainScript", t) - } - } - - private fun Path.addSubProject(project: String, buildScriptText: String) { - resolve(project) - .createDirectory() - .resolve("build.gradle") - .writeText(buildScriptText) - } -} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt deleted file mode 100644 index 1123baddb..000000000 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/NoopExecutor.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.executor - -import com.github.jengelman.gradle.plugins.shadow.fixture.SnippetFixture -import java.nio.file.Path - -object NoopExecutor : SnippetExecutor { - override val fixture: SnippetFixture get() = error("NoopExecutor does not have a fixture.") - override fun execute(tempDir: Path, snippet: String) = Unit -} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt deleted file mode 100644 index 0b445e007..000000000 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/executor/SnippetExecutor.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.executor - -import com.github.jengelman.gradle.plugins.shadow.fixture.SnippetFixture -import java.nio.file.Path - -interface SnippetExecutor { - val fixture: SnippetFixture - - fun execute(tempDir: Path, snippet: String) -} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt deleted file mode 100644 index 5cc82e008..000000000 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/GroovyDslFixture.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.fixture - -object GroovyDslFixture : SnippetFixture { - - override val pluginsBlock: String = """ - plugins { - id 'java' - id 'com.gradleup.shadow' - } - """.trimIndent() - - val importsExtractor: (String) -> List = { snippet -> - val imports = StringBuilder() - val scriptMinusImports = StringBuilder() - - snippet.lines().forEach { line -> - val target = if (line.trim().startsWith("import ")) imports else scriptMinusImports - target.append(line).append("\n") - } - - listOf( - imports.toString(), - // Replace the version placeholders. - scriptMinusImports.toString().replace("", "+"), - ) - } -} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt deleted file mode 100644 index d39fc2386..000000000 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/fixture/SnippetFixture.kt +++ /dev/null @@ -1,5 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.fixture - -interface SnippetFixture { - val pluginsBlock: String -} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt new file mode 100644 index 000000000..b67399f5f --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt @@ -0,0 +1,61 @@ +package com.github.jengelman.gradle.plugins.shadow.snippet + +import java.nio.file.Path +import java.util.regex.Pattern +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.Path +import kotlin.io.path.name +import kotlin.io.path.pathString +import kotlin.io.path.readText +import kotlin.io.path.relativeTo +import kotlin.io.path.walk + +object CodeSnippetExtractor { + private val docRoot = Path(System.getProperty("DOCS_DIR")) + + @OptIn(ExperimentalPathApi::class) + private val markdownPaths = docRoot.walk() + .filter { it.name.endsWith(".md", ignoreCase = true) } + .toList() + + fun extract(lang: DslLang): List { + return markdownPaths.flatMap { path -> + createExecutables(lang, path) + } + } + + private fun createExecutables( + lang: DslLang, + markdownPath: Path, + ): List { + val relativeDocPath = markdownPath.relativeTo(docRoot).pathString + return createSnippets(markdownPath.readText(), lang).map { (lineNumber, snippet) -> + SnippetExecutable.create( + lang, + snippet, + "$relativeDocPath:$lineNumber", + ) { + RuntimeException("The error line in the doc is near ${markdownPath.toUri()}:$lineNumber", it) + } + } + } + + private fun createSnippets(source: String, lang: DslLang) = buildMap { + val pattern = Pattern.compile("(?ims)```${lang}\n(.*?)\n```") + val matcher = pattern.matcher(source) + + while (matcher.find()) { + val line = source.lineNumberAt(matcher.start()) + val code = matcher.group(1) + put(line, code) + } + } + + private fun String.lineNumberAt(index: Int): Int { + var line = 1 + for (i in 0 until index.coerceAtMost(length)) { + if (this[i] == '\n') line++ + } + return line + } +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt new file mode 100644 index 000000000..b39a34616 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt @@ -0,0 +1,9 @@ +package com.github.jengelman.gradle.plugins.shadow.snippet + +enum class DslLang { +// Kotlin, + Groovy, + ; + + override fun toString(): String = name.lowercase() +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt new file mode 100644 index 000000000..3982579d8 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt @@ -0,0 +1,19 @@ +package com.github.jengelman.gradle.plugins.shadow.snippet + +class GroovyBuildExecutable( + override val snippet: String, + override val displayName: String, + override val exceptionTransformer: (Throwable) -> Throwable, +) : SnippetExecutable() { + + override val lang: DslLang = DslLang.Groovy + + override val buildScriptName: String = "build.gradle" + + override val pluginsBlock: String = """ + plugins { + id 'java' + id 'com.gradleup.shadow' + } + """.trimIndent() +} diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt new file mode 100644 index 000000000..aeb0b31d6 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt @@ -0,0 +1,22 @@ +@file:Suppress("ktlint:standard:no-empty-file") + +package com.github.jengelman.gradle.plugins.shadow.snippet +/* +class KotlinBuildExecutable( + override val snippet: String, + override val displayName: String, + override val exceptionTransformer: (Throwable) -> Throwable, +) : SnippetExecutable() { + + override val lang: DslLang = DslLang.Kotlin + + override val buildScriptName: String = "build.gradle.kts" + + override val pluginsBlock: String = """ + plugins { + java + id("com.gradleup.shadow") + } + """.trimIndent() +} +*/ diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt new file mode 100644 index 000000000..6031fc419 --- /dev/null +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -0,0 +1,105 @@ +package com.github.jengelman.gradle.plugins.shadow.snippet + +import java.lang.System.lineSeparator +import java.nio.file.Path +import kotlin.io.path.createDirectory +import kotlin.io.path.writeText +import org.gradle.testkit.runner.GradleRunner +import org.junit.jupiter.api.function.Executable + +sealed class SnippetExecutable : Executable { + abstract val lang: DslLang + abstract val buildScriptName: String + abstract val pluginsBlock: String + + abstract val snippet: String + abstract val displayName: String + abstract val exceptionTransformer: (Throwable) -> Throwable + + lateinit var tempDir: Path + + override fun execute() { + try { + execute(tempDir, snippet) + } catch (t: Throwable) { + throw exceptionTransformer(t) + } + } + + private fun execute(projectRoot: Path, snippet: String) { + projectRoot.resolve("settings.gradle").writeText( + """ + dependencyResolutionManagement { + repositories { + mavenLocal() + mavenCentral() + } + } + include 'api', 'main' + """.trimIndent(), + ) + projectRoot.addSubProject("api", pluginsBlock) + + val (imports, withoutImports) = importsExtractor(snippet) + val mainScript = buildString { + append(imports) + append(lineSeparator()) + // All buildscript {} blocks must appear before any plugins {} blocks in the script. + if (withoutImports.contains("buildscript {")) { + append(withoutImports) + } else { + if (!withoutImports.contains("plugins {")) { + append(pluginsBlock) + append(lineSeparator()) + } + append(withoutImports) + } + append(lineSeparator()) + }.trimIndent() + projectRoot.addSubProject("main", mainScript) + + try { + GradleRunner.create() + .withProjectDir(projectRoot.toFile()) + .withPluginClasspath() + .forwardOutput() + .withArguments("--warning-mode=fail", "build") + .build() + } catch (t: Throwable) { + throw RuntimeException("Failed to execute snippet:\n\n$mainScript", t) + } + } + + private fun Path.addSubProject(project: String, buildScriptText: String) { + resolve(project) + .createDirectory() + .resolve(buildScriptName) + .writeText(buildScriptText) + } + + private fun importsExtractor(snippet: String): Pair { + val imports = StringBuilder() + val withoutImports = StringBuilder() + + snippet.lines().forEach { line -> + val target = if (line.trim().startsWith("import ")) imports else withoutImports + target.append(line).append(lineSeparator()) + } + + return imports.toString() to + // Replace the version placeholders. + withoutImports.toString().replace("", "+") + } + + companion object { + fun create( + lang: DslLang, + snippet: String, + testName: String, + exceptionTransformer: (Throwable) -> Throwable, + ): SnippetExecutable = when (lang) { + DslLang.Groovy -> GroovyBuildExecutable(snippet, testName, exceptionTransformer) +// DslLang.Kotlin -> KotlinBuildExecutable(snippet, testName, exceptionTransformer) + } + } +} From eeb2a1371577bc409fe720b079ee0b461c29c410 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Mar 2025 01:33:15 -0500 Subject: [PATCH 308/941] Fix sub unordered lists (#1311) They were not honored by pymdownx. --- docs/getting-started/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 02ba9f034..0157f121d 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -63,15 +63,15 @@ following behavior: * Configures the `shadowJar` task to bundle all dependencies from the `runtimeClasspath` configuration. * Configures the _classifier_ attribute of the `shadowJar` task to be `'all'` . * Configures the `shadowJar` task to generate a `Manifest` with: - * Inheriting all configuration from the standard `jar` task. - * Adds a `Class-Path` attribute to the `Manifest` that appends all dependencies from the `shadow` configuration + * Inheriting all configuration from the standard `jar` task. + * Adds a `Class-Path` attribute to the `Manifest` that appends all dependencies from the `shadow` configuration * Configures the `shadowJar` task to _exclude_ any JAR index or cryptographic signature files matching the following patterns: - * `META-INF/INDEX.LIST` - * `META-INF/*.SF` - * `META-INF/*.DSA` - * `META-INF/*.RSA` - * `META-INF/versions/**/module-info.class` - * `module-info.class` + * `META-INF/INDEX.LIST` + * `META-INF/*.SF` + * `META-INF/*.DSA` + * `META-INF/*.RSA` + * `META-INF/versions/**/module-info.class` + * `module-info.class` * Creates and registers the `shadow` component in the project (used for integrating with `maven-publish`). ## Shadowing Gradle Plugins From 22f6e98deb84ad347cf6cab54ed321f1bd0bfc8c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 18:23:54 +0800 Subject: [PATCH 309/941] Update dependency com.vanniktech:gradle-maven-publish-plugin to v0.31.0 (#1312) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a2cea5e65..fcba8d048 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.1" -mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:0.30.0" +mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:0.31.0" jetbrains-dokka = "org.jetbrains.dokka:dokka-gradle-plugin:2.0.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" } From dca39326e12f9e50c90362bfb547b08a7c9db98f Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 6 Mar 2025 23:56:29 +0800 Subject: [PATCH 310/941] Fix release link for v9.0.0-beta10 --- docs/changes/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 9af5785bc..2f47d5d39 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -596,7 +596,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Thu, 6 Mar 2025 11:03:15 -0500 Subject: [PATCH 311/941] Add Kotlin DSL examples in docs (#1306) * Update docs/application-plugin/README.md * Update snippet parser and add Kotlin DSL runner * Update docs/configuration/dependencies/README.md * Update docs/configuration/filtering/README.md * Update docs/configuration/merging/README.md * Update docs/configuration/minimizing/README.md * Update docs/configuration/relocation/README.md * Update docs/configuration/reproducible-builds/README.md * Update docs/configuration/README.md * Update docs/custom-tasks/README.md * Update docs/getting-started/README.md * Update docs/kmp-plugin/README.md * Update docs/multi-project/README.md * Update docs/plugins/README.md * Update docs/publishing/README.md * Trim spaces * Fix tabbed details * No need to apply java plugin for application plugin * Cleanups --- docs/application-plugin/README.md | 82 +++++- docs/changes/README.md | 8 +- docs/configuration/README.md | 79 +++++- docs/configuration/dependencies/README.md | 140 +++++++++- docs/configuration/filtering/README.md | 27 ++ docs/configuration/merging/README.md | 256 ++++++++++++++---- docs/configuration/minimizing/README.md | 40 +++ docs/configuration/relocation/README.md | 58 +++- .../reproducible-builds/README.md | 13 + docs/custom-tasks/README.md | 40 ++- docs/getting-started/README.md | 84 +++++- docs/kmp-plugin/README.md | 44 ++- docs/multi-project/README.md | 12 + docs/plugins/README.md | 31 ++- docs/publishing/README.md | 141 ++++++++-- .../gradle/plugins/shadow/JavaPluginTest.kt | 8 +- .../gradle/plugins/shadow/PublishingTest.kt | 8 +- .../shadow/snippet/CodeSnippetExtractor.kt | 2 +- .../gradle/plugins/shadow/snippet/DslLang.kt | 2 +- .../shadow/snippet/KotlinBuildExecutable.kt | 5 +- .../shadow/snippet/SnippetExecutable.kt | 2 +- 21 files changed, 953 insertions(+), 129 deletions(-) diff --git a/docs/application-plugin/README.md b/docs/application-plugin/README.md index bb4db86b9..09a33b116 100644 --- a/docs/application-plugin/README.md +++ b/docs/application-plugin/README.md @@ -7,15 +7,31 @@ configure additional tasks for running the shadowed JAR and creating distributio Just like the normal `jar` task, when the `application` plugin is applied, the `shadowJar` manifest will be configured to contain the `Main-Class` attribute with the value specified in the project's `mainClassName` attribute. +=== "Kotlin" + + ```kotlin + plugins { + application + id("com.gradleup.shadow") + } + + application { + mainClass = "myapp.Main" + } + ``` + +=== "Groovy" + + ```groovy plugins { - id 'java' id 'application' id 'com.gradleup.shadow' } - + application { mainClass = 'myapp.Main' } + ``` ## Running the Shadow JAR @@ -25,21 +41,43 @@ The `runShadow` task is a [`JavaExec`](https://docs.gradle.org/current/dsl/org.g task that is configured to execute `java -jar myproject-all.jar`. It can be configured the same as any other `JavaExec` task. +=== "Kotlin" + + ```kotlin + plugins { + application + id("com.gradleup.shadow") + } + + application { + mainClass = "myapp.Main" + // Optionally, you can add default JVM arguments to the start scripts like this: + applicationDefaultJvmArgs = listOf("--add-opens=java.base/java.lang=ALL-UNNAMED") + } + + tasks.runShadow { + args("foo") + } + ``` + +=== "Groovy" + + ```groovy plugins { - id 'java' id 'application' id 'com.gradleup.shadow' } - + application { mainClass = 'myapp.Main' // Optionally, you can add default JVM arguments to the start scripts like this: applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] } - + tasks.named('runShadow', JavaExec) { args 'foo' } + ``` ## Distributing the Shadow JAR @@ -54,12 +92,39 @@ files for a distribution to `build/install/-shadow/`. You can also add more files into the distribution like: +=== "Kotlin" + + ```kotlin + plugins { + application + id("com.gradleup.shadow") + } + + application { + mainClass = "myapp.Main" + // Optionally, you can include `some/dir` files in the distribution like this: + applicationDistribution.from("some/dir") { + include("*.txt") + } + } + + // `shadow` is the name of the distribution created by Shadow plugin + distributions.named("shadow") { + // Optionally, you can add more files into extra directory in the distribution like this: + contents.from("extra/echo.sh") { + into("extra") + } + } + ``` + +=== "Groovy" + + ```groovy plugins { - id 'java' id 'application' id 'com.gradleup.shadow' } - + application { mainClass = 'myapp.Main' // Optionally, you can include `some/dir` files in the distribution like this: @@ -67,7 +132,7 @@ You can also add more files into the distribution like: include '*.txt' } } - + // `shadow` is the name of the distribution created by Shadow plugin distributions.named('shadow') { // Optionally, you can add more files into extra directory in the distribution like this: @@ -75,6 +140,7 @@ You can also add more files into the distribution like: into 'extra' } } + ``` View [the official doc described](https://docs.gradle.org/current/userguide/distribution_plugin.html#distribution_plugin) for more information about configuring distributions. diff --git a/docs/changes/README.md b/docs/changes/README.md index 2f47d5d39..46b16e976 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Added** + +- Add Kotlin DSL examples in docs. ([#1306](https://github.com/GradleUp/shadow/pull/1306)) + **Changed** - **BREAKING CHANGE:** Polish `ShadowSpec`. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) @@ -311,9 +315,7 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " While not being able to filter entire transitive dependency graphs might seem like an oversight, it is necessary because it would not be possible to intelligently determine the build author's intended results when there is a @@ -66,51 +110,115 @@ Dependencies can be filtered using regex patterns. Coupled with the `::` notation for dependencies, this allows for excluding/including using any of these individual fields. +=== "Kotlin" + + ```kotlin + dependencies { + implementation("org.apache.logging.log4j:log4j-core:2.11.1") + } + tasks.shadowJar { + dependencies { + exclude(dependency("org.apache.logging.log4j:log4j-core:.*")) + } + } + ``` + +=== "Groovy" + + ```groovy dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('org.apache.logging.log4j:log4j-core:.*')) } } + ``` Any of the individual fields can be safely absent and will function as though a wildcard was specified. +=== "Kotlin" + + ```kotlin + dependencies { + implementation("org.apache.logging.log4j:log4j-core:2.11.1") + } + tasks.shadowJar { + dependencies { + exclude(dependency(":org.apache.logging.log4j:log4j-core")) + } + } + ``` + +=== "Groovy" + + ```groovy dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('org.apache.logging.log4j:log4j-core')) } } + ``` The above code snippet is functionally equivalent to the previous example. This same pattern can be used for any of the dependency notation fields. +=== "Kotlin" + + ```kotlin + dependencies { + implementation("org.apache.logging.log4j:log4j-core:2.11.1") + } + tasks.shadowJar { + dependencies { + exclude(dependency(":log4j-core:2.11.1")) + } + } + ``` + +=== "Groovy" + + ```groovy dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency(':log4j-core:2.11.1')) } } + ``` + +=== "Kotlin" + ```kotlin + dependencies { + implementation("org.apache.logging.log4j:log4j-core:2.11.1") + } + tasks.shadowJar { + dependencies { + exclude(dependency("org.apache.logging.log4j:2.11.1")) + } + } + ``` + +=== "Groovy" + + ```groovy dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('org.apache.logging.log4j:2.11.1')) } } + ``` ### Programmatically Selecting Dependencies to Filter @@ -118,10 +226,27 @@ If more complex decisions are needed to select the dependencies to be included, [`dependencies`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html) block provides a method that accepts a `Closure` for selecting dependencies. +=== "Kotlin" + + ```kotlin + dependencies { + implementation("org.apache.logging.log4j:log4j-core:2.11.1") + } + tasks.shadowJar { + dependencies { + exclude { + it.moduleGroup == "org.apache.logging.log4j" + } + } + } + ``` + +=== "Groovy" + + ```groovy dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude { @@ -129,3 +254,4 @@ block provides a method that accepts a `Closure` for selecting dependencies. } } } + ``` diff --git a/docs/configuration/filtering/README.md b/docs/configuration/filtering/README.md index 93aa6e880..767077298 100644 --- a/docs/configuration/filtering/README.md +++ b/docs/configuration/filtering/README.md @@ -11,15 +11,42 @@ contents. This means that, the configuration is applied to the individual files from both the project source set or _any_ of the dependencies to be merged. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + exclude("a2.properties") + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { exclude 'a2.properties' } + ``` + Excludes and includes can be combined just like a normal `Jar` task, with `excludes` taking precedence over `includes`. Additionally, ANT style patterns can be used to match multiple files. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + include("*.jar") + include("*.properties") + exclude("a2.properties") + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { include '*.jar' include '*.properties' exclude 'a2.properties' } + ``` diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 45bedef0d..f975e53a1 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -8,91 +8,136 @@ entry in the JAR before being written to the final output JAR. This allows a [`Transformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-transformer/index.html) to determine if it should process a particular entry and apply any modifications before writing the stream to the output. +=== "Kotlin" + + ```kotlin + import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer + import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext + import org.apache.tools.zip.ZipOutputStream + import org.gradle.api.file.FileTreeElement + + class MyTransformer : ResourceTransformer { + override fun canTransformResource(element: FileTreeElement): Boolean = true + override fun transform(context: TransformerContext) {} + override fun hasTransformedResource(): Boolean = true + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {} + } + + tasks.shadowJar { + transform() + } + ``` + +=== "Groovy" + + ```groovy import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext - import javax.annotation.Nonnull import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement - + class MyTransformer implements ResourceTransformer { - @Override - boolean canTransformResource(@Nonnull FileTreeElement element) { return true } - - @Override - void transform(@Nonnull TransformerContext context) {} - - @Override - boolean hasTransformedResource() { return true } - - @Override - void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} - } - + @Override boolean canTransformResource(FileTreeElement element) { return true } + @Override void transform(TransformerContext context) {} + @Override boolean hasTransformedResource() { return true } + @Override void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {} + } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(MyTransformer.class) } + ``` Additionally, a `Transformer` can accept a `Closure` to configure the provided `Transformer`. +=== "Kotlin" + + ```kotlin + import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer + import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext + import org.apache.tools.zip.ZipOutputStream + import org.gradle.api.file.FileTreeElement + + class MyTransformer(var enabled: Boolean = false) : ResourceTransformer { + override fun canTransformResource(element: FileTreeElement): Boolean = true + override fun transform(context: TransformerContext) {} + override fun hasTransformedResource(): Boolean = true + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {} + } + + tasks.shadowJar { + transform() { + enabled = true + } + } + ``` + +=== "Groovy" + + ```groovy import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext - import javax.annotation.Nonnull import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement - + class MyTransformer implements ResourceTransformer { boolean enabled - - @Override - boolean canTransformResource(@Nonnull FileTreeElement element) { return true } - - @Override - void transform(@Nonnull TransformerContext context) {} - - @Override - boolean hasTransformedResource() { return true } - - @Override - void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} - } - + @Override boolean canTransformResource(FileTreeElement element) { return true } + @Override void transform(TransformerContext context) {} + @Override boolean hasTransformedResource() { return true } + @Override void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {} + } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(MyTransformer.class) { enabled = true } } + ``` An instantiated instance of a `Transformer` can also be provided. +=== "Kotlin" + + ```kotlin + import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer + import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext + import org.apache.tools.zip.ZipOutputStream + import org.gradle.api.file.FileTreeElement + + class MyTransformer(val enabled: Boolean) : ResourceTransformer { + override fun canTransformResource(element: FileTreeElement): Boolean = true + override fun transform(context: TransformerContext) {} + override fun hasTransformedResource(): Boolean = true + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {} + } + + tasks.shadowJar { + transform(MyTransformer(true)) + } + ``` + +=== "Groovy" + + ```groovy import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext - import javax.annotation.Nonnull import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement - + class MyTransformer implements ResourceTransformer { final boolean enabled - - MyTransformer(boolean enabled) { - this.enabled = enabled - } - - @Override - boolean canTransformResource(@Nonnull FileTreeElement element) { return true } - - @Override - void transform(@Nonnull TransformerContext context) {} - - @Override - boolean hasTransformedResource() { return true } - - @Override - void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {} - } - + MyTransformer(boolean enabled) { this.enabled = enabled } + @Override boolean canTransformResource(FileTreeElement element) { return true } + @Override void transform(TransformerContext context) {} + @Override boolean hasTransformedResource() { return true } + @Override void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {} + } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(new MyTransformer(true)) } + ``` ## Merging Service Descriptor Files @@ -106,9 +151,21 @@ The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github class is used to perform this merging. By default, it will merge each copy of a file under `META-INF/services` into a single file in the output JAR. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + mergeServiceFiles() + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { mergeServiceFiles() } + ``` The above code snippet is a convenience syntax for calling [`transform(ServiceFileTransformer.class)`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html). @@ -125,22 +182,50 @@ By default the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow is configured to merge files in `META-INF/services`. This directory can be overridden to merge descriptor files in a different location. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + mergeServiceFiles { + path = "META-INF/custom" + } + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { mergeServiceFiles { path = 'META-INF/custom' } } + ``` #### Excluding/Including Specific Service Descriptor Files From Merging The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) class supports specifying specific files to include or exclude from merging. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + mergeServiceFiles { + exclude("META-INF/services/com.acme.*") + } + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { mergeServiceFiles { exclude 'META-INF/services/com.acme.*' } } + ``` ## Merging Groovy Extension Modules @@ -151,18 +236,42 @@ will handle these files. The [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task also provides a short syntax method to add this transformer. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + mergeGroovyExtensionModules() + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { mergeGroovyExtensionModules() } + ``` ## Merging Log4j2 Plugin Cache Files (`Log4j2Plugins.dat`) `Log4j2PluginsCacheFileTransformer` is a `Transformer` that merges `META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat` plugin caches from all the jars containing Log4j 2.x Core components. It's a Gradle equivalent of [Log4j Plugin Descriptor Transformer](https://logging.apache.org/log4j/transform/log4j-transform-maven-shade-plugin-extensions.html#log4j-plugin-cache-transformer). +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + transform() + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer.class) } + ``` ## Appending Text Files @@ -174,10 +283,39 @@ method of [`append(String)`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html) to configure this transformer. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + append("test.properties") + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { append 'test.properties' } + ``` + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + // short syntax + append("resources/application.yml", "\n---\n") + // full syntax + transform() { + resource = "resources/custom-config/application.yml" + separator = "\n---\n" + } + } + ``` + +=== "Groovy" + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { // short syntax append('resources/application.yml', '\n---\n') @@ -187,6 +325,8 @@ configure this transformer. separator = '\n---\n' } } + ``` + ## Appending XML Files @@ -196,8 +336,22 @@ reads each XML document and merges each root element into a single document. There is no short syntax method for the [`XmlAppendingTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html). It must be added using the [`transform`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html)) methods. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + transform() { + resource = "properties.xml" + } + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { transform(com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer.class) { resource = 'properties.xml' } } + ``` diff --git a/docs/configuration/minimizing/README.md b/docs/configuration/minimizing/README.md index 2f9f84286..ffa56b6d9 100644 --- a/docs/configuration/minimizing/README.md +++ b/docs/configuration/minimizing/README.md @@ -2,30 +2,70 @@ Shadow can automatically remove all classes of dependencies that are not used by the project, thereby minimizing the resulting shadowed JAR. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + minimize() + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { minimize() } + ``` A dependency can be excluded from the minimization process, thereby forcing it's inclusion the shadow JAR. This is useful when the dependency analyzer cannot find the usage of a class programmatically, for example if the class is loaded dynamically via `Class.forName(String)`. Each of the `group`, `name` and `version` fields separated by `:` of a `dependency` is interpreted as a regular expression. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + minimize { + exclude(dependency("org.scala-lang:.*:.*")) + } + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { minimize { exclude(dependency('org.scala-lang:.*:.*')) } } + ``` > Dependencies scoped as `api` will automatically excluded from minimization and used as "entry points" on minimization. Similar to dependencies, projects can also be excluded. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + minimize { + exclude(project(":api")) + } + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { minimize { exclude(project(":api")) } } + ``` > When excluding a `project`, all dependencies of the excluded `project` are automatically excluded as well. diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 8261706e7..215c51125 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -10,9 +10,21 @@ Shadow uses the ASM library to modify class byte code to replace the package nam statements for a class. Any non-class files that are stored within a package structure are also relocated to the new location. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + relocate("junit.framework", "shadow.junit") + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { relocate 'junit.framework', 'shadow.junit' } + ``` The code snippet will rewrite the location for any class in the `junit.framework` to be `shadow.junit`. For example, the class `junit.framework.TestCase` becomes `shadow.junit.TestCase`. @@ -33,6 +45,22 @@ Specific classes or files can be `included`/`excluded` from the relocation opera [Ant Path Matcher](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html) syntax to specify matching path for your files and directories. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + relocate("junit.textui", "a") { + exclude("junit.textui.TestRunner") + } + relocate("junit.framework", "b") { + include("junit.framework.Test*") + } + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { relocate('junit.textui', 'a') { exclude 'junit.textui.TestRunner' @@ -41,15 +69,30 @@ syntax to specify matching path for your files and directories. include 'junit.framework.Test*' } } + ``` For a more advanced path matching you might want to use [Regular Expressions](https://regexr.com/) instead. Wrap the expresion in `%regex[]` before passing it to `include`/`exclude`. - + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + relocate("org.foo", "a") { + include("%regex[org/foo/.*Factory[0-9].*]") + } + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { relocate('org.foo', 'a') { include '%regex[org/foo/.*Factory[0-9].*]' } } + ``` ## Automatically Relocating Dependencies @@ -60,10 +103,23 @@ removed for clarity reasons in version 4.0.0. To configure automatic dependency relocation, set `enableRelocation = true` and optionally specify a custom `relocationPrefix` to override the default value of `"shadow"`. +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + enableRelocation = true + relocationPrefix = "myapp" + } + ``` + +=== "Groovy" + + ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { enableRelocation = true relocationPrefix = "myapp" } + ``` In versions before 8.1.0 it was necessary to configure a separate `ConfigureShadowRelocation` task for this. diff --git a/docs/configuration/reproducible-builds/README.md b/docs/configuration/reproducible-builds/README.md index 00bd16f14..5287b6ab6 100644 --- a/docs/configuration/reproducible-builds/README.md +++ b/docs/configuration/reproducible-builds/README.md @@ -2,10 +2,23 @@ By default, JAR files generated by Gradle (with or without Shadow) for a single project with the same source code may not be identical to each other. Sometimes it's desireable to configure a project to consistently output a byte-for-byte identical JAR on every build. Gradle supports this with the following configuration, and Shadow will correctly respect these settings too: +=== "Kotlin" + + ```kotlin + tasks.withType().configureEach { + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + } + ``` + +=== "Groovy" + + ```groovy tasks.withType(AbstractArchiveTask).configureEach { preserveFileTimestamps = false reproducibleFileOrder = true } + ``` One effect that this configuration will have is that the timestamps of all files in the JAR will be reset to a single consistent value. If your code or any files being included into the JAR depend on the timestamps being set accurately within the JAR, then this may not be the correct choice for you. diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index 911df990c..253fae1e3 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -5,25 +5,53 @@ It is possible to add arbitrary [`ShadowJar`](https://gradleup.com/shadow/api/sh tasks to a project. When doing so, ensure that the `configurations` property is specified to inform Shadow which dependencies to merge into the output. - def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { +=== "Kotlin" + + ```kotlin + val testShadowJar by tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME description = "Create a combined JAR of project and test dependencies" - + archiveClassifier = "tests" - from sourceSets.test.output - configurations = [project.configurations.testRuntimeClasspath] - + from(sourceSets.test.map { it.output }) + configurations = provider { listOf(project.configurations["testRuntimeClasspath"]) } + + manifest { + // Optionally, set the main class for the JAR. + attributes(mapOf("Main-Class" to "test.Main")) + // You can also set other attributes here. + } + } + + // Optionally, make the `assemble` task depend on the new task. + tasks.assemble { + dependsOn(testShadowJar) + } + ``` + +=== "Groovy" + + ```groovy + def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + description = 'Create a combined JAR of project and test dependencies' + + archiveClassifier = 'tests' + from sourceSets.named('test').map { it.output } + configurations = provider { [project.configurations.testRuntimeClasspath] } + manifest { // Optionally, set the main class for the JAR. attributes 'Main-Class': 'test.Main' // You can also set other attributes here. } } - + // Optionally, make the `assemble` task depend on the new task. tasks.named('assemble') { dependsOn testShadowJar } + ``` The code snippet above will generate a shadowed JAR containing both the `main` and `test` sources as well as all `testRuntimeOnly` and `testImplementation` dependencies. diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 0157f121d..1b3ab5ba6 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -1,32 +1,96 @@ # Getting Started +=== "Kotlin" + + ```kotlin + plugins { + java + id("com.gradleup.shadow") version "" + } + ``` + +=== "Groovy" + + ```groovy plugins { id 'java' id 'com.gradleup.shadow' version '' } + ``` Alternatively, the plugin can be added to the buildscript classpath and applied: +=== "Kotlin" + + ```kotlin buildscript { repositories { + mavenCentral() + gradlePluginPortal() + } + dependencies { + classpath("com.gradleup.shadow:shadow-gradle-plugin:") + } + } + // `apply plugin` stuffs are used with `buildscript`. + apply(plugin = "java") + apply(plugin = "com.gradleup.shadow") + ``` + +=== "Groovy" + + ```groovy + buildscript { + repositories { + mavenCentral() gradlePluginPortal() } dependencies { classpath 'com.gradleup.shadow:shadow-gradle-plugin:' } } - // `apply plugin` stuffs are used with `buildscript`. apply plugin: 'java' apply plugin: 'com.gradleup.shadow' + ``` + +===! "Kotlin" + +
+ Snapshots of the development version are available in + + Sonatype's snapshots repository. + +

+ + ```kotlin + buildscript { + repositories { + mavenCentral() + maven("https://oss.sonatype.org/content/repositories/snapshots/") + } + dependencies { + classpath("com.gradleup.shadow:shadow-gradle-plugin:") + } + } + // `apply plugin` stuffs are used with `buildscript`. + apply(plugin = "java") + apply(plugin = "com.gradleup.shadow") + ``` + +

+
+ +=== "Groovy" -
-Snapshots of the development version are available in - -Sonatype's snapshots repository. - -

+

+ Snapshots of the development version are available in + + Sonatype's snapshots repository. + +

+ ```groovy buildscript { repositories { mavenCentral() @@ -36,13 +100,13 @@ Sonatype's snapshots repository. classpath 'com.gradleup.shadow:shadow-gradle-plugin:' } } - // `apply plugin` stuffs are used with `buildscript`. apply plugin: 'java' apply plugin: 'com.gradleup.shadow' + ``` -

-
+

+
**NOTE:** The correct maven coordinates for each version of Shadow can be found by referencing the Gradle Plugin documentation [here](https://plugins.gradle.org/plugin/com.gradleup.shadow). diff --git a/docs/kmp-plugin/README.md b/docs/kmp-plugin/README.md index 33b551801..8c7c682e2 100644 --- a/docs/kmp-plugin/README.md +++ b/docs/kmp-plugin/README.md @@ -4,13 +4,50 @@ 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 its `jvm` target. +=== "Kotlin" + + ```kotlin + plugins { + kotlin("multiplatform") + id("com.gradleup.shadow") + } + + val ktorVersion = "3.1.0" + + kotlin { + jvm() + sourceSets { + val commonMain by getting { + dependencies { + implementation("io.ktor:ktor-client-core:$ktorVersion") + } + } + val jvmMain by getting { + dependencies { + implementation("io.ktor:ktor-client-okhttp:$ktorVersion") + } + } + } + } + + tasks.shadowJar { + manifest { + // Optionally, set the main class for the shadowed JAR. + attributes["Main-Class"] = "com.example.MainKt" + } + } + ``` + +=== "Groovy" + + ```groovy plugins { id 'org.jetbrains.kotlin.multiplatform' id 'com.gradleup.shadow' } - + def ktorVersion = "3.1.0" - + kotlin { jvm() sourceSets { @@ -26,10 +63,11 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. } } } - + 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' } } + ``` diff --git a/docs/multi-project/README.md b/docs/multi-project/README.md index aa412bed8..4b2efeeeb 100644 --- a/docs/multi-project/README.md +++ b/docs/multi-project/README.md @@ -12,6 +12,18 @@ requires the shadowed JAR as a dependency. In this case, use Gradle's normal dependency declaration mechanism to depend on the `shadow` configuration of the shadowed project. +=== "Kotlin" + + ```kotlin + dependencies { + implementation(project(path = ":api", configuration = "shadow")) + } + ``` + +=== "Groovy" + + ```groovy dependencies { implementation project(path: ':api', configuration: 'shadow') } + ``` diff --git a/docs/plugins/README.md b/docs/plugins/README.md index 3dde6678e..ba5ee0a53 100644 --- a/docs/plugins/README.md +++ b/docs/plugins/README.md @@ -10,11 +10,37 @@ and `relocationPrefix` settings on any `ShadowJar` task. A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the `shadowJar` task for relocation. +=== "Kotlin" + + ```kotlin + plugins { + `java-gradle-plugin` // May have to apply the latest `com.gradle.plugin-publish` for better publishing support. + id("com.gradleup.shadow") + } + + dependencies { + implementation("org.jdom:jdom2:2.0.6") + implementation("org.ow2.asm:asm:6.0") + implementation("org.ow2.asm:asm-commons:6.0") + implementation("commons-io:commons-io:2.4") + implementation("org.apache.ant:ant:1.9.4") + implementation("org.codehaus.plexus:plexus-utils:2.0.6") + } + + tasks.shadowJar { + enableRelocation = true + archiveClassifier = "" + } + ``` + +=== "Groovy" + + ```groovy plugins { id 'java-gradle-plugin' // May have to apply the latest `com.gradle.plugin-publish` for better publishing support. id 'com.gradleup.shadow' } - + dependencies { implementation 'org.jdom:jdom2:2.0.6' implementation 'org.ow2.asm:asm:6.0' @@ -23,11 +49,12 @@ A simple Gradle plugin can use this feature by applying the `shadow` plugin and implementation 'org.apache.ant:ant:1.9.4' implementation 'org.codehaus.plexus:plexus-utils:2.0.6' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { enableRelocation = true archiveClassifier = '' } + ``` ## Publishing shadowed Gradle plugins The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. diff --git a/docs/publishing/README.md b/docs/publishing/README.md index b32cf1985..6bf2126db 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -7,24 +7,47 @@ The Shadow plugin will automatically configure the necessary tasks in the presen The plugin provides the `shadow` component to configure the publication with the necessary artifact and dependencies in the POM file. +=== "Kotlin" + + ```kotlin + plugins { + java + `maven-publish` + id("com.gradleup.shadow") + } + + publishing { + publications { + create("shadow") { + from(components["shadow"]) + } + } + repositories { + maven("https://repo.myorg.com") + } + } + ``` + +=== "Groovy" + + ```groovy plugins { id 'java' id 'maven-publish' id 'com.gradleup.shadow' } - + publishing { publications { shadow(MavenPublication) { - from(components.shadow) // or components["shadow"] in Kotlin DSL + from components.shadow } } repositories { - maven { - url = "https://repo.myorg.com" - } + maven { url = 'https://repo.myorg.com' } } } + ``` ## Shadow Configuration and Publishing @@ -50,15 +73,55 @@ be manually configured. You may want to publish the shadowed JAR instead of the original JAR. This can be done by trimming the `archiveClassifier` of the shadowed JAR like the following: +=== "Kotlin" + + ```kotlin + plugins { + java + `maven-publish` + id("com.gradleup.shadow") + } + + group = "shadow" + version = "1.0" + + dependencies { + // This will be bundled in the shadowed JAR and not declared in the POM. + implementation("some:a:1.0") + // This will be excluded + shadow("some:b:1.0") + // This will be excluded + compileOnly("some:c:1.0") + } + + tasks.shadowJar { + archiveClassifier = "" + } + + publishing { + publications { + create("shadow") { + from(components["shadow"]) + } + } + repositories { + maven("https://repo.myorg.com") + } + } + ``` + +=== "Groovy" + + ```groovy plugins { id 'java' id 'maven-publish' id 'com.gradleup.shadow' } - + group = 'shadow' version = '1.0' - + dependencies { // This will be bundled in the shadowed JAR and not declared in the POM. implementation 'some:a:1.0' @@ -68,11 +131,11 @@ the `archiveClassifier` of the shadowed JAR like the following: // This will be excluded from the shadowed JAR and not declared in the POM or `META-INF/MANIFEST.MF`. compileOnly 'some:c:1.0' } - + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { archiveClassifier = '' } - + publishing { publications { shadow(MavenPublication) { @@ -80,35 +143,70 @@ the `archiveClassifier` of the shadowed JAR like the following: } } repositories { - maven { - url = "https://repo.myorg.com" - } + maven { url = 'https://repo.myorg.com' } } } + ``` ## Publish Custom ShadowJar Task Outputs It is possible to publish a custom `ShadowJar` task's output via the [`MavenPublication.artifact(java.lang.Object)`](https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html#org.gradle.api.publish.maven.MavenPublication:artifact(java.lang.Object)) method. +=== "Kotlin" + + ```kotlin + plugins { + java + `maven-publish` + id("com.gradleup.shadow") + } + + val testShadowJar by tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { + group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + description = "Create a combined JAR of project and test dependencies" + archiveClassifier = "tests" + from(sourceSets.test.map { it.output }) + configurations = provider { listOf(project.configurations["testRuntimeClasspath"]) } + } + + dependencies { + testImplementation("junit:junit:3.8.2") + } + + publishing { + publications { + create("shadow") { + artifact(testShadowJar) + } + } + repositories { + maven("https://repo.myorg.com") + } + } + ``` + +=== "Groovy" + + ```groovy plugins { id 'java' id 'maven-publish' id 'com.gradleup.shadow' } - + def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME - description = "Create a combined JAR of project and test dependencies" - archiveClassifier = "tests" - from sourceSets.test.output - configurations = [project.configurations.testRuntimeClasspath] + description = 'Create a combined JAR of project and test dependencies' + archiveClassifier = 'tests' + from sourceSets.named('test').map { it.output } + configurations = provider { [project.configurations.testRuntimeClasspath] } } - + dependencies { testImplementation 'junit:junit:3.8.2' } - + publishing { publications { shadow(MavenPublication) { @@ -116,8 +214,7 @@ It is possible to publish a custom `ShadowJar` task's output via the [`MavenPubl } } repositories { - maven { - url = "https://repo.myorg.com" - } + maven { url = 'https://repo.myorg.com' } } } + ``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 1e7666abf..3cce5d71e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -515,10 +515,10 @@ class JavaPluginTest : BasePluginTest() { } def $testShadowJarTask = tasks.register('$testShadowJarTask', ${ShadowJar::class.java.name}) { group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME - description = "Create a combined JAR of project and test dependencies" - archiveClassifier = "tests" - from sourceSets.test.output - configurations = [project.configurations.testRuntimeClasspath] + description = 'Create a combined JAR of project and test dependencies' + archiveClassifier = 'tests' + from sourceSets.named('test').map { it.output } + configurations = provider { [project.configurations.testRuntimeClasspath] } manifest { attributes '$mainClassAttributeKey': 'my.Main' } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 12d3c83a7..ca6fa94ae 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -148,10 +148,10 @@ class PublishingTest : BasePluginTest() { projectBlock = """ def testShadowJar = tasks.register('testShadowJar', ${ShadowJar::class.java.name}) { group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME - description = "Create a combined JAR of project and test dependencies" - archiveClassifier = "tests" - from sourceSets.test.output - configurations = [project.configurations.testRuntimeClasspath] + description = 'Create a combined JAR of project and test dependencies' + archiveClassifier = 'tests' + from sourceSets.named('test').map { it.output } + configurations = provider { [project.configurations.testRuntimeClasspath] } } """.trimIndent(), dependenciesBlock = """ diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt index b67399f5f..5047a05fd 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt @@ -41,7 +41,7 @@ object CodeSnippetExtractor { } private fun createSnippets(source: String, lang: DslLang) = buildMap { - val pattern = Pattern.compile("(?ims)```${lang}\n(.*?)\n```") + val pattern = Pattern.compile("(?ims) {4}```${lang}\n(.*?)\n {4}```") val matcher = pattern.matcher(source) while (matcher.find()) { diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt index b39a34616..8179417cc 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.snippet enum class DslLang { -// Kotlin, + Kotlin, Groovy, ; diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt index aeb0b31d6..3dd74cdcb 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt @@ -1,7 +1,5 @@ -@file:Suppress("ktlint:standard:no-empty-file") - package com.github.jengelman.gradle.plugins.shadow.snippet -/* + class KotlinBuildExecutable( override val snippet: String, override val displayName: String, @@ -19,4 +17,3 @@ class KotlinBuildExecutable( } """.trimIndent() } -*/ diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index 6031fc419..311f1305e 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -99,7 +99,7 @@ sealed class SnippetExecutable : Executable { exceptionTransformer: (Throwable) -> Throwable, ): SnippetExecutable = when (lang) { DslLang.Groovy -> GroovyBuildExecutable(snippet, testName, exceptionTransformer) -// DslLang.Kotlin -> KotlinBuildExecutable(snippet, testName, exceptionTransformer) + DslLang.Kotlin -> KotlinBuildExecutable(snippet, testName, exceptionTransformer) } } } From 11af32b2307b39dd572c3f72e7ee6981f98a38a7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Mar 2025 20:28:44 -0500 Subject: [PATCH 312/941] Test post-pattern case for relocatePath (#1315) --- .../gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 4a10937a0..53f391e0e 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -184,6 +184,9 @@ class SimpleRelocatorTest { var relocator = SimpleRelocator("org.foo") assertThat(relocator.relocatePath("org/foo/bar/Class.class")) .isEqualTo("hidden/org/foo/bar/Class.class") + // Post-pattern case. + assertThat(relocator.relocatePath("org/foosssssss/bar/Class.class")) + .isEqualTo("hidden/org/foosssssss/bar/Class.class") relocator = SimpleRelocator("org.foo", "private.stuff") assertThat(relocator.relocatePath("org/foo/bar/Class.class")) From a37bf0eb0367d444b034b0ca3368aa1330950218 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 7 Mar 2025 09:35:07 +0800 Subject: [PATCH 313/941] Unify plugin names All "Gradle Shadow" things should be renamed to "Shadow" or "Shadow Gradle Plugin". --- NOTICE | 6 +++--- README.md | 2 +- docs/README.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/NOTICE b/NOTICE index 8c8d7431a..8ea18eaef 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ -Gradle-Shadow-Plugin +Shadow-Gradle-Plugin Copyright (c) 2013 John Engelman All Rights Reserved. -This product is licensed to you under the Apache License, Version 2.0 (the "License"). -You may not use this product except in compliance with the License. \ No newline at end of file +This product is licensed to you under the Apache License, Version 2.0 (the "License"). +You may not use this product except in compliance with the License. diff --git a/README.md b/README.md index e9b8e7e06..f8bac5613 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Gradle Shadow +# Shadow Gradle plugin for creating fat/uber JARs with support for package relocation. diff --git a/docs/README.md b/docs/README.md index 7ddf3c213..6cd9f05ec 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,5 +1,5 @@
- Gradle Shadow Plugin + Shadow Gradle Plugin

Shadow Gradle Plugin

The library author's dependency toolkit

From 4bc680113a12385bfdd13e1d026071b3c1197f2b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Mar 2025 21:24:18 -0500 Subject: [PATCH 314/941] Update NOTICE file and minor adjustments (#1316) * Update NOTICE * Update .gitignore * Delete site folder in clean task * Should be Shadow contributors * Cleanups --- .gitignore | 33 ++++++++++++++++++--------------- NOTICE | 4 ++-- build.gradle.kts | 12 ++++++++---- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/.gitignore b/.gitignore index 11f395974..38247f4a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,19 +1,22 @@ -*.un~ +.fleet +.gradle +.kotlin +build + +### IntelliJ IDEA ### +.idea +*.iws *.iml *.ipr -*.iws -.idea -build -.gradle -*.orig out -.gradletasknamecache -.gradle-test-kit -classes/ + +### VS Code ### +.vscode +bin + +### Mac OS ### .DS_Store -jd-gui.cfg -bin/ -.vscode/ -.kotlin -# Generated by Dokka, it's depended on by MkDocs. -docs/api/ + +### MkDocs ### +docs/api +site diff --git a/NOTICE b/NOTICE index 8ea18eaef..ae19c72f5 100644 --- a/NOTICE +++ b/NOTICE @@ -1,5 +1,5 @@ -Shadow-Gradle-Plugin -Copyright (c) 2013 John Engelman All Rights Reserved. +Shadow Gradle Plugin +Copyright (c) 2013-2025 John Engelman and Shadow contributors. All Rights Reserved. This product is licensed to you under the Apache License, Version 2.0 (the "License"). You may not use this product except in compliance with the License. diff --git a/build.gradle.kts b/build.gradle.kts index a5c640076..1dec260cc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -172,8 +172,12 @@ tasks.clean { val includedBuilds = gradle.includedBuilds dependsOn(includedBuilds.map { it.task(path) }) - val dirs = includedBuilds.map { it.projectDir } + projectDir - delete.addAll(dirs.map { it.resolve(".gradle") }) - delete.addAll(dirs.map { it.resolve(".kotlin") }) - delete.add(tasks.dokkaHtml.map { it.outputDirectory }) + val rootDirs = includedBuilds.map { it.projectDir } + projectDir + delete += listOf( + rootDirs.map { it.resolve(".gradle") }, + rootDirs.map { it.resolve(".kotlin") }, + tasks.dokkaHtml.map { it.outputDirectory }, + // Generated by MkDocs. + rootDir.resolve("site"), + ) } From 8a8620a3e44dde5e8478ee29ba5364093b37b407 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Mar 2025 21:32:00 -0500 Subject: [PATCH 315/941] Fix typos in docs (#1317) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs/about/README.md | 8 ++++---- docs/configuration/README.md | 4 ++-- docs/configuration/dependencies/README.md | 5 ++--- docs/configuration/merging/README.md | 2 +- docs/configuration/minimizing/README.md | 4 ++-- docs/configuration/relocation/README.md | 2 +- docs/configuration/reproducible-builds/README.md | 2 +- docs/getting-started/README.md | 2 +- docs/introduction/README.md | 2 +- 9 files changed, 15 insertions(+), 16 deletions(-) diff --git a/docs/about/README.md b/docs/about/README.md index cbcdfedc7..749eb959f 100644 --- a/docs/about/README.md +++ b/docs/about/README.md @@ -1,13 +1,13 @@ # About This Project -I started this project in December of 2012. We were working on converting from a monolithic application into the +I started this project in December 2012. We were working on converting from a monolithic application into the new hot jazz of "microservices" using Dropwizard. I had also just started learning about Gradle and I knew that the incremental build system it provided would benefit our development team greatly. Unfortunately, the closest thing that Gradle had to Maven's Shade plugin was its ability to create application TARs and ZIPs. -So, Charlie Knudsen and myself set out to port the existing Shade code into a Gradle plugin. +So, Charlie Knudsen and I (John Engelman) set out to port the existing Shade code into a Gradle plugin. This port is what existed up until the `0.9` milestone releases for Shadow. It functioned, but it wasn't idiomatic Gradle by any means. @@ -23,5 +23,5 @@ so Shadow was published there. ## Contributors - - \ No newline at end of file + Contributors + diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 9aee78ee8..6e385102d 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -44,10 +44,10 @@ As with all `Jar` tasks in Gradle, these values can be overridden: ## Configuring the Runtime Classpath -Each Java JAR file contains a manifest file that provides meta data about the contents of the JAR file itself. +Each Java JAR file contains a manifest file that provides metadata about the contents of the JAR file itself. When using a shadowed JAR file as an executable JAR, it is assumed that all necessary runtime classes are contained within the JAR itself. -There may be situations where the desire is to **not** bundle select dependencies into the shadowed JAR file but +There may be situations where the desire is to **not** bundle select dependencies into the shadowed JAR file, but they are still required for runtime execution. In these scenarios, Shadow creates a `shadow` configuration to declare these dependencies. diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index ae07ed610..92bf039da 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -26,9 +26,8 @@ This means any dependency declared in the `runtimeOnly` configuration would be * > Note the literal use of `project.configurations` when setting the `configurations` attribute of a [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. -This is **required**. It maybe be tempting to specify `configurations = [configurations.compile]` but this will not -have the intended effect, as `configurations.compile` will try to delegate to the `configurations` property of the -the [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task instead of the `project` +This is **required**. It may be tempting to specify `configurations = [configurations.compile]` but this will not +have the intended effect, as `configurations.compile` will try to delegate to the `configurations` property of the [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task instead of the `project` ## Embedding Jar Files Inside Your Shadow Jar diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index f975e53a1..b7b72b2dc 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -178,7 +178,7 @@ these files if your dependencies contain them. ### Configuring the Location of Service Descriptor Files -By default the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) +By default, the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) is configured to merge files in `META-INF/services`. This directory can be overridden to merge descriptor files in a different location. diff --git a/docs/configuration/minimizing/README.md b/docs/configuration/minimizing/README.md index ffa56b6d9..8eafc2321 100644 --- a/docs/configuration/minimizing/README.md +++ b/docs/configuration/minimizing/README.md @@ -18,7 +18,7 @@ Shadow can automatically remove all classes of dependencies that are not used by } ``` -A dependency can be excluded from the minimization process, thereby forcing it's inclusion the shadow JAR. +A dependency can be excluded from the minimization process, thereby forcing its inclusion the shadow JAR. This is useful when the dependency analyzer cannot find the usage of a class programmatically, for example if the class is loaded dynamically via `Class.forName(String)`. Each of the `group`, `name` and `version` fields separated by `:` of a `dependency` is interpreted as a regular expression. @@ -43,7 +43,7 @@ a `dependency` is interpreted as a regular expression. } ``` -> Dependencies scoped as `api` will automatically excluded from minimization and used as "entry points" on minimization. +> Dependencies scoped as `api` will be automatically excluded from minimization and used as "entry points" on minimization. Similar to dependencies, projects can also be excluded. diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 215c51125..333e9cc5a 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -71,7 +71,7 @@ syntax to specify matching path for your files and directories. } ``` -For a more advanced path matching you might want to use [Regular Expressions](https://regexr.com/) instead. Wrap the expresion in `%regex[]` before +For a more advanced path matching you might want to use [Regular Expressions](https://regexr.com/) instead. Wrap the expression in `%regex[]` before passing it to `include`/`exclude`. === "Kotlin" diff --git a/docs/configuration/reproducible-builds/README.md b/docs/configuration/reproducible-builds/README.md index 5287b6ab6..9beaa5894 100644 --- a/docs/configuration/reproducible-builds/README.md +++ b/docs/configuration/reproducible-builds/README.md @@ -1,6 +1,6 @@ # Reproducible Builds -By default, JAR files generated by Gradle (with or without Shadow) for a single project with the same source code may not be identical to each other. Sometimes it's desireable to configure a project to consistently output a byte-for-byte identical JAR on every build. Gradle supports this with the following configuration, and Shadow will correctly respect these settings too: +By default, JAR files generated by Gradle (with or without Shadow) for a single project with the same source code may not be identical to each other. Sometimes it's desirable to configure a project to consistently output a byte-for-byte identical JAR on every build. Gradle supports this with the following configuration, and Shadow will correctly respect these settings too: === "Kotlin" diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 1b3ab5ba6..1d4806f66 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -143,4 +143,4 @@ following behavior: Shadow ships with a companion task that can be used to automatically discover dependency packages and configure them for relocation. This is useful in projects if you want to relocate all dependencies. -For more details see the section [Using Shadow to Package Gradle Plugins](/plugins/) +For more details see the section [Using Shadow to Package Gradle Plugins](../plugins/README.md) diff --git a/docs/introduction/README.md b/docs/introduction/README.md index 21060564c..dbdf4ab3f 100644 --- a/docs/introduction/README.md +++ b/docs/introduction/README.md @@ -11,7 +11,7 @@ into the output jar without incurring the I/O overhead of expanding the jars to Shadowing a project output has 2 major use cases: 1. Creating an _executable_ JAR distribution -1. Bundling and relocating common dependencies in libraries to avoid classpath conflicts +2. Bundling and relocating common dependencies in libraries to avoid classpath conflicts ### Executable Distributions From 1b57fc8ef5a8d43cdc0990f5608ca99603f58e56 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 7 Mar 2025 10:42:58 +0800 Subject: [PATCH 316/941] Add deploy workflow and remove force push flags --- .github/workflows/deploy.yml | 19 +++++++++++++++++++ .github/workflows/release.yml | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy.yml diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 000000000..8035c6e9f --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,19 @@ +name: Deploy + +on: + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + if: github.repository == 'GradleUp/shadow' + permissions: + contents: write + steps: + - uses: actions/checkout@v4 + - name: Deploy site + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + pip install mkdocs-material + mkdocs gh-deploy diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0a30bacb0..221d0d3e1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,7 +34,7 @@ jobs: git config --global user.name 'github-actions[bot]' git config --global user.email 'github-actions[bot]@users.noreply.github.com' pip install mkdocs-material - mkdocs gh-deploy --force + mkdocs gh-deploy - name: Extract release notes # TODO: replace this after https://github.com/ffurrer2/extract-release-notes/pull/355 is merged. uses: Goooler/extract-release-notes@6e686e7a607d03716b7cff561371a82065b22c33 From 2cdc112d477d35642c2f82fa2c23489ad42fcbaf Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 7 Mar 2025 10:46:49 +0800 Subject: [PATCH 317/941] Fetch all branches for MkDocs --- .github/workflows/deploy.yml | 3 +++ .github/workflows/release.yml | 3 +++ 2 files changed, 6 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8035c6e9f..47f7ba848 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -11,6 +11,9 @@ jobs: contents: write steps: - uses: actions/checkout@v4 + with: + # Fetch all tags and branches, so that MkDocs could push incremental updates. + fetch-depth: 0 - name: Deploy site run: | git config --global user.name 'github-actions[bot]' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 221d0d3e1..745999dfa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,9 @@ jobs: contents: write steps: - uses: actions/checkout@v4 + with: + # Fetch all tags and branches, so that MkDocs could push incremental updates. + fetch-depth: 0 - uses: actions/setup-java@v4 with: distribution: 'zulu' From feb55582967141a8ac89a55f408e130c17d0fb8c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Mar 2025 22:13:19 -0500 Subject: [PATCH 318/941] Reuse deploy steps via composite action (#1318) https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-composite-action --- .github/actions/deploy-site/action.yml | 15 +++++++++++++++ .github/workflows/deploy.yml | 7 +------ .github/workflows/release.yml | 7 +------ 3 files changed, 17 insertions(+), 12 deletions(-) create mode 100644 .github/actions/deploy-site/action.yml diff --git a/.github/actions/deploy-site/action.yml b/.github/actions/deploy-site/action.yml new file mode 100644 index 000000000..8ef83d06d --- /dev/null +++ b/.github/actions/deploy-site/action.yml @@ -0,0 +1,15 @@ +name: 'Deploy Site' +description: 'Deploy site to GitHub Pages' + +runs: + using: 'composite' + steps: + - name: Deploy Site + shell: bash + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + # Don't cache it to track updates. + pip install mkdocs-material + # Incremental pushes, make sure gh-pages branch is fetched by checkout. + mkdocs gh-deploy diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 47f7ba848..64ab05649 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,9 +14,4 @@ jobs: with: # Fetch all tags and branches, so that MkDocs could push incremental updates. fetch-depth: 0 - - name: Deploy site - run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' - pip install mkdocs-material - mkdocs gh-deploy + - uses: ./.github/actions/deploy-site diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 745999dfa..1e236cb4e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -32,12 +32,7 @@ jobs: ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} - - name: Deploy site - run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' - pip install mkdocs-material - mkdocs gh-deploy + - uses: ./.github/actions/deploy-site - name: Extract release notes # TODO: replace this after https://github.com/ffurrer2/extract-release-notes/pull/355 is merged. uses: Goooler/extract-release-notes@6e686e7a607d03716b7cff561371a82065b22c33 From b9d4a93656f1ba66929195e2e244bbd1edcc589c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 6 Mar 2025 22:37:23 -0500 Subject: [PATCH 319/941] Update compatibility matrix (#1319) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index f8bac5613..edc69efe2 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,13 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. [![CI](https://github.com/GradleUp/shadow/actions/workflows/ci.yml/badge.svg?branch=main&event=push)](https://github.com/GradleUp/shadow/actions/workflows/ci.yml?query=branch:main+event:push) [![License](https://img.shields.io/github/license/GradleUp/shadow.svg)](LICENSE) -## Latest Test Compatibility - -| Gradle Version | Shadow Version | -|----------------|----------------| -| 5.x | 5.2.0 - 6.0.0 | -| 6.x | 5.2.0 - 6.1.0 | -| 7.x | 7.0.0+ | -| 8.0 - 8.2.x | 8.0.0 - 8.1.1 | -| 8.3+ | 8.3.0+ | - -**NOTE**: Shadow v5.+ is compatible with Gradle 5.x - 6.x and Java 7 - 15 _only_, v6.1.0+ requires Java 8+. +## Compatibility Matrix + +| Shadow Version | Min Gradle Version | Min Java Version | +|----------------|--------------------|------------------| +| 5.2.0 - 6.1.0 | 5.x - 6.x | 7 | +| 6.1.0+ | 6.x | 8 | +| 7.0.0+ | 7.x | 8 | +| 8.0.0+ | 8.0+ | 8 | +| 8.3.0+ | 8.3+ | 8 | +| 9.0.0+ | 8.3+ | 11 | From 6f1f666f927c799eee413b5faf3b982991ed42c4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Mar 2025 01:52:52 -0500 Subject: [PATCH 320/941] Fix relocation exclusion for file patterns like kotlin/kotlin.kotlin_builtins (#1313) * Test `preserveKotlinBuiltins` * It passes for `include` * Revert "It passes for `include`" This reverts commit 7986ffe9d6de8d080d6434877eb3bf0cddf39172. * Flag withDebug * Compat file pattern like `kotlin/kotlin.kotlin_builtins` * Revert "Flag withDebug" This reverts commit e94328053374ec0b56bd69d42584d87c8ab43d17. * Note issues * Update changelog * Add a unit test case * Use to `FilenameUtils` avoid `InvalidPathException` on Windows * Rename classPattern to filePattern * Check the file without extension --- docs/changes/README.md | 4 +++ .../gradle/plugins/shadow/RelocationTest.kt | 35 +++++++++++++++++++ .../shadow/relocation/SimpleRelocator.kt | 16 ++++++--- .../shadow/relocation/SimpleRelocatorTest.kt | 8 +++++ 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 46b16e976..e26e172c2 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -15,6 +15,10 @@ - Overload `relocate`, `transform` and things for better usability in Kotlin. - **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) +**Fixed** + +- Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) + ## [v9.0.0-beta10] (2025-03-05) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 6a0f6177b..99feaacf0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -432,6 +432,41 @@ class RelocationTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/295", + "https://github.com/GradleUp/shadow/issues/562", + "https://github.com/GradleUp/shadow/issues/884", + ) + @Test + fun preserveKotlinBuiltins() { + val kotlinJar = buildJar("kotlin.jar") { + insert("kotlin/kotlin.kotlin_builtins", "This is a Kotlin builtins file.") + } + projectScriptPath.appendText( + """ + dependencies { + ${implementationFiles(kotlinJar)} + } + $shadowJar { + relocate('kotlin.', 'foo.kotlin.') { + exclude('kotlin/kotlin.kotlin_builtins') + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries( + "kotlin/kotlin.kotlin_builtins", + ) + doesNotContainEntries( + "foo/kotlin/kotlin.kotlin_builtins", + ) + } + } + private companion object { @JvmStatic fun prefixProvider() = listOf( diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 9ca8297ae..c5ee0e436 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import java.util.regex.Pattern +import org.apache.commons.io.FilenameUtils import org.codehaus.plexus.util.SelectorUtils import org.gradle.api.tasks.Input @@ -199,12 +200,19 @@ public open class SimpleRelocator @JvmOverloads constructor( continue } - val classPattern = pattern.replace('.', '/') - add(classPattern) + val fileName = FilenameUtils.getName(pattern) + val fileParent = FilenameUtils.getPathNoEndSeparator(pattern) + val filePattern = if (!fileParent.isNullOrEmpty() && fileName.isNotEmpty()) { + // It's a file pattern like `kotlin/kotlin.kotlin_builtins`, so we don't need to normalize it. + pattern + } else { + pattern.replace('.', '/') + } + add(filePattern) // Actually, class patterns should just use 'foo.bar.*' ending with a single asterisk, but some users // mistake them for path patterns like 'my/path/**', so let us be a bit more lenient here. - if (classPattern.endsWith("/*") || classPattern.endsWith("/**")) { - val packagePattern = classPattern.substring(0, classPattern.lastIndexOf('/')) + if (filePattern.endsWith("/*") || filePattern.endsWith("/**")) { + val packagePattern = filePattern.substring(0, filePattern.lastIndexOf('/')) add(packagePattern) } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 53f391e0e..11fd55e46 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -67,6 +67,14 @@ class SimpleRelocatorTest { assertThat(relocator.canRelocatePath("test")).isFalse() // shorter than path pattern with / assertThat(relocator.canRelocatePath("org/f")).isTrue() // equal to path pattern assertThat(relocator.canRelocatePath("/org/f")).isTrue() // equal to path pattern with / + + relocator = SimpleRelocator("foo.") + assertThat(relocator.canRelocatePath("foo/foo.bar")).isTrue() + relocator.exclude("foo/foo.bar") + assertThat(relocator.canRelocatePath("foo/foo.bar")).isFalse() // Don't handle file path pattern. + assertThat(relocator.canRelocatePath("foo/foobar")).isTrue() + relocator.exclude("foo/foobar") + assertThat(relocator.canRelocatePath("foo/foobar")).isFalse() // File without extension. } @Test From ac7546715b1f73d931815f6c332e65a0a9b75a98 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Mar 2025 02:24:18 -0500 Subject: [PATCH 321/941] Tidy up RelocateClassContext (#1320) * Use relocateClass extensions * Merge context classes * Publish extensions --- api/shadow.api | 5 +++++ .../shadow/internal/RelocatorRemapper.kt | 2 ++ .../gradle/plugins/shadow/internal/Utils.kt | 11 ----------- .../shadow/relocation/RelocateClassContext.kt | 6 ------ .../shadow/relocation/RelocatePathContext.kt | 6 ------ .../shadow/relocation/RelocationContext.kt | 19 +++++++++++++++++++ .../ComponentsXmlResourceTransformer.kt | 5 ++--- .../Log4j2PluginsCacheFileTransformer.kt | 5 ++--- .../transformers/ServiceFileTransformer.kt | 8 +++----- .../shadow/relocation/SimpleRelocatorTest.kt | 2 -- 10 files changed, 33 insertions(+), 36 deletions(-) delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt diff --git a/api/shadow.api b/api/shadow.api index bc41314e7..ddb51769e 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -109,6 +109,11 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat public final synthetic fun unbox-impl ()Ljava/lang/String; } +public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContextKt { + public static final fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;Ljava/lang/String;)Ljava/lang/String; + public static final fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;Ljava/lang/String;)Ljava/lang/String; +} + public abstract interface class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator$Companion; public abstract fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index a35352ced..42c0e69d1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -1,6 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.internal import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass +import com.github.jengelman.gradle.plugins.shadow.relocation.relocatePath import java.util.regex.Pattern import org.objectweb.asm.commons.Remapper diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 05732babd..7f692ed55 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -1,8 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.internal -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocatePathContext -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream @@ -52,14 +49,6 @@ internal inline fun zipEntry( block() } -internal fun Relocator.relocatePath(path: String): String { - return relocatePath(RelocatePathContext(path)) -} - -internal fun Relocator.relocateClass(className: String): String { - return relocateClass(RelocateClassContext(className)) -} - internal fun Properties.inputStream( charset: Charset = Charsets.ISO_8859_1, comments: String = "", diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt deleted file mode 100644 index 7a85ec702..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -@JvmInline -public value class RelocateClassContext( - public val className: String, -) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt deleted file mode 100644 index 7fc499606..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.relocation - -@JvmInline -public value class RelocatePathContext( - public val path: String, -) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt new file mode 100644 index 000000000..48f8afa4e --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt @@ -0,0 +1,19 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +@JvmInline +public value class RelocateClassContext( + public val className: String, +) + +@JvmInline +public value class RelocatePathContext( + public val path: String, +) + +public fun Relocator.relocateClass(className: String): String { + return relocateClass(RelocateClassContext(className)) +} + +public fun Relocator.relocatePath(path: String): String { + return relocatePath(RelocatePathContext(path)) +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index b7c29ebb7..ab9503b69 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import java.io.BufferedInputStream import java.io.ByteArrayOutputStream import java.io.IOException @@ -108,8 +108,7 @@ public open class ComponentsXmlResourceTransformer : ResourceTransformer { if (!className.isNullOrEmpty()) { for (relocator in context.relocators) { if (relocator.canRelocateClass(className)) { - val relocateClassContext = RelocateClassContext(className) - return relocator.relocateClass(relocateClassContext) + return relocator.relocateClass(className) } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index d36c1d1e2..8f78196c7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -1,8 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import java.net.URL import java.nio.file.Path import java.util.Collections @@ -69,10 +69,9 @@ public open class Log4j2PluginsCacheFileTransformer : ResourceTransformer { pluginCache.allCategories.values.forEach { currentMap -> currentMap.values.forEach { currentPluginEntry -> val className = currentPluginEntry.className - val relocateClassContext = RelocateClassContext(className) tempRelocators.firstOrNull { it.canRelocateClass(className) }?.let { relocator -> // Then we perform that relocation and update the plugin entry to reflect the new value. - currentPluginEntry.className = relocator.relocateClass(relocateClassContext) + currentPluginEntry.className = relocator.relocateClass(className) } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 8532c3ef4..dbd7627e6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry -import com.github.jengelman.gradle.plugins.shadow.relocation.RelocateClassContext +import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement @@ -48,8 +48,7 @@ public open class ServiceFileTransformer( var resource = context.path.substringAfter("$path/") context.relocators.forEach { relocator -> if (relocator.canRelocateClass(resource)) { - val classContext = RelocateClassContext(className = resource) - resource = relocator.relocateClass(classContext) + resource = relocator.relocateClass(resource) return@forEach } } @@ -60,8 +59,7 @@ public open class ServiceFileTransformer( var line = it context.relocators.forEach { relocator -> if (relocator.canRelocateClass(line)) { - val lineContext = RelocateClassContext(line) - line = relocator.relocateClass(lineContext) + line = relocator.relocateClass(line) } } out.add(line) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 11fd55e46..4141b675b 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -4,8 +4,6 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.internal.relocateClass -import com.github.jengelman.gradle.plugins.shadow.internal.relocatePath import org.junit.jupiter.api.Test /** From 706ff36bee374394b0a7c73077387414fb582958 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Mar 2025 03:14:24 -0500 Subject: [PATCH 322/941] Shorten state checks (#1321) --- .../gradle/plugins/shadow/util/AppendableMavenRepository.kt | 2 +- .../github/jengelman/gradle/plugins/shadow/util/JarPath.kt | 2 +- .../github/jengelman/gradle/plugins/shadow/util/RunProcess.kt | 4 ++-- .../gradle/plugins/shadow/transformers/TransformerContext.kt | 2 +- .../plugins/shadow/transformers/ServiceFileTransformerTest.kt | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index f4956cde3..5793e0ef2 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -124,7 +124,7 @@ class AppendableMavenRepository( } fun build(): Path { - if (!::existingJar.isInitialized) error("No jar file provided for $coordinate") + check(::existingJar.isInitialized) { "No jar file provided for $coordinate" } return existingJar.also { check(it.exists()) { "Jar file doesn't exist for $coordinate in: $it" } check(it.isRegularFile()) { "Jar is not a regular file for $coordinate in: $it" } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index ffd80d546..13302d12d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -30,7 +30,7 @@ fun ZipFile.getContent(entryName: String): String { } fun ZipFile.getStream(entryName: String): InputStream { - val entry = getEntry(entryName) ?: error("Entry $entryName not found in all entries: ${entries().toList()}") + val entry = requireNotNull(getEntry(entryName)) { "Entry $entryName not found in all entries: ${entries().toList()}" } return getInputStream(entry) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt index c15413598..d9e3f5218 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt @@ -9,8 +9,8 @@ fun runProcess( val err = process.errorStream.bufferedReader().use { it.readText() } val out = process.inputStream.bufferedReader().use { it.readText() } - if (exitCode != 0 || err.isNotEmpty()) { - error("Error occurred when running command line: $err") + check(exitCode == 0 && err.isEmpty()) { + "Error occurred when running command line: $err" } return out diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt index 59c412e18..71e3f5cfa 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -18,7 +18,7 @@ public data class TransformerContext @JvmOverloads constructor( public fun relocators(relocators: Set): Builder = apply { this.relocators = relocators } public fun build(): TransformerContext = TransformerContext( path = path, - inputStream = inputStream ?: error("inputStream is required"), + inputStream = requireNotNull(inputStream) { "inputStream is required" }, relocators = relocators, ) } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 1b59c60be..28f539c8d 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -158,7 +158,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() } fun ZipFile.getStream(entryName: String): InputStream { - val entry = getEntry(entryName) ?: error("Entry $entryName not found in all entries: ${entries().toList()}") + val entry = requireNotNull(getEntry(entryName)) { "Entry $entryName not found in all entries: ${entries().toList()}" } return getInputStream(entry) } From 5e211a9d7fde9f6f86d0a102d6884fab7ce4681f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 7 Mar 2025 04:13:34 -0500 Subject: [PATCH 323/941] Rearrange task outcome and caching tests (#1323) * A new FilteringCachingTest * Tweak contains checks * Use assertCompositeExecutions * Remove SUCCESS check for canRegisterCustomShadowJarTask * Remove FAILED check for failBuildIfProcessingBadJar * Remove FAILED check for honorDuplicatesStrategyWithThrowing * Remove configurationCachingOfConfigurationsIsUpToDate as no inputs changed * Fix style * Reduce all assertions --- .../gradle/plugins/shadow/FilteringTest.kt | 65 +----- .../gradle/plugins/shadow/JavaPluginTest.kt | 46 +--- .../gradle/plugins/shadow/PublishingTest.kt | 6 +- .../shadow/caching/FilteringCachingTest.kt | 210 ++++++++++++++++++ .../shadow/caching/ShadowJarCachingTest.kt | 113 ---------- .../ServiceFileTransformerTest.kt | 7 +- 6 files changed, 225 insertions(+), 222 deletions(-) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index bb3d2f5e7..0863939d5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -4,9 +4,7 @@ import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import kotlin.io.path.appendText -import kotlin.io.path.readText import kotlin.io.path.writeText -import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test @@ -91,57 +89,6 @@ class FilteringTest : BasePluginTest() { commonAssertions() } - @Test - fun dependencyExclusionsAffectUpToDateCheck() { - dependOnAndExcludeArtifactD() - - run(shadowJarTask) - - commonAssertions() - - val replaced = projectScriptPath.readText() - .replace("exclude(dependency('my:d:1.0'))", "exclude(dependency('my:c:1.0'))") - projectScriptPath.writeText(replaced) - val result = run(shadowJarTask) - - assertThat(result).taskOutcomeEquals(shadowJarTask, SUCCESS) - assertThat(outputShadowJar).useAll { - val entries = entriesInAB + "d.properties" - containsEntries(*entries) - doesNotContainEntries( - "c.properties", - ) - } - } - - @Test - fun projectExclusionsAffectUpToDateCheck() { - dependOnAndExcludeArtifactD() - - run(shadowJarTask) - - commonAssertions() - - val replaced = projectScriptPath.readText() - .replace("exclude(dependency('my:d:1.0'))", "exclude 'a.properties'") - projectScriptPath.writeText(replaced) - - val result = run(shadowJarTask) - - assertThat(result).taskOutcomeEquals(shadowJarTask, SUCCESS) - assertThat(outputShadowJar).useAll { - containsEntries( - "a2.properties", - "b.properties", - "c.properties", - "d.properties", - ) - doesNotContainEntries( - "a.properties", - ) - } - } - @Test fun includeDependencyAndExcludeOthers() { projectScriptPath.appendText( @@ -170,8 +117,10 @@ class FilteringTest : BasePluginTest() { "d.properties", "my/Passed.class", ) - val entries = entriesInAB + "c.properties" - doesNotContainEntries(*entries) + doesNotContainEntries( + *entriesInAB, + "c.properties", + ) } } @@ -273,8 +222,10 @@ class FilteringTest : BasePluginTest() { private fun commonAssertions() { assertThat(outputShadowJar).useAll { - val entries = entriesInAB + "c.properties" - containsEntries(*entries) + containsEntries( + *entriesInAB, + "c.properties", + ) doesNotContainEntries( "d.properties", ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 3cce5d71e..0bf8409df 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -31,9 +31,6 @@ import kotlin.io.path.outputStream import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin import org.gradle.testfixtures.ProjectBuilder -import org.gradle.testkit.runner.TaskOutcome.FAILED -import org.gradle.testkit.runner.TaskOutcome.SUCCESS -import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledForJreRange import org.junit.jupiter.api.condition.JRE @@ -526,9 +523,8 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - val result = run(testShadowJarTask) + run(testShadowJarTask) - assertThat(result).taskOutcomeEquals(":$testShadowJarTask", SUCCESS) assertThat(jarPath("build/libs/my-1.0-tests.jar")).useAll { containsEntries( mainClass, @@ -545,41 +541,6 @@ class JavaPluginTest : BasePluginTest() { ) } - @Test - fun configurationCachingOfConfigurationsIsUpToDate() { - settingsScriptPath.appendText( - """ - include 'lib' - """.trimIndent(), - ) - projectScriptPath.writeText("") - - path("lib/src/main/java/lib/Lib.java").writeText( - """ - package lib; - public class Lib {} - """.trimIndent(), - ) - path("lib/build.gradle").writeText( - """ - ${getDefaultProjectBuildScript()} - dependencies { - implementation 'junit:junit:3.8.2' - } - $shadowJar { - configurations = [project.configurations.compileClasspath] - } - """.trimIndent(), - ) - - val libShadowJarTask = ":lib:$SHADOW_JAR_TASK_NAME" - run(libShadowJarTask) - val result = run(libShadowJarTask) - - assertThat(result).taskOutcomeEquals(libShadowJarTask, UP_TO_DATE) - assertThat(result.output).contains("Reusing configuration cache.") - } - @Issue( "https://github.com/GradleUp/shadow/issues/915", ) @@ -599,10 +560,7 @@ class JavaPluginTest : BasePluginTest() { val result = runWithFailure(shadowJarTask) - assertThat(result).all { - taskOutcomeEquals(shadowJarTask, FAILED) - transform { it.output }.containsMatch("Cannot expand ZIP '.*bad\\.jar'".toRegex()) - } + assertThat(result.output).containsMatch("Cannot expand ZIP '.*bad\\.jar'".toRegex()) } @Test diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index ca6fa94ae..8711ce0be 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -278,8 +278,10 @@ class PublishingTest : BasePluginTest() { "aa.properties", "aa2.properties", ) - val entries = entriesInAB + "bb.properties" - doesNotContainEntries(*entries) + doesNotContainEntries( + *entriesInAB, + "bb.properties", + ) } assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module"))) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt new file mode 100644 index 000000000..df26e0ada --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt @@ -0,0 +1,210 @@ +package com.github.jengelman.gradle.plugins.shadow.caching + +import assertk.Assert +import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.JarPath +import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import kotlin.io.path.appendText +import kotlin.io.path.readText +import kotlin.io.path.writeText +import org.junit.jupiter.api.BeforeAll +import org.junit.jupiter.api.Test + +class FilteringCachingTest : BaseCachingTest() { + @BeforeAll + override fun doFirst() { + super.doFirst() + publishArtifactCD() + } + + @Test + fun dependencyExclusionsAffectUpToDateCheck() { + dependOnAndExcludeArtifactD() + + assertCompositeExecutions { + commonAssertions() + } + + val replaced = projectScriptPath.readText() + .replace("exclude(dependency('my:d:1.0'))", "exclude(dependency('my:c:1.0'))") + projectScriptPath.writeText(replaced) + + assertCompositeExecutions { + containsEntries( + *entriesInAB, + "d.properties", + ) + doesNotContainEntries( + "c.properties", + ) + } + } + + @Test + fun projectExclusionsAffectUpToDateCheck() { + dependOnAndExcludeArtifactD() + + assertCompositeExecutions { + commonAssertions() + } + + val replaced = projectScriptPath.readText() + .replace("exclude(dependency('my:d:1.0'))", "exclude 'a.properties'") + projectScriptPath.writeText(replaced) + + assertCompositeExecutions { + containsEntries( + "a2.properties", + "b.properties", + "c.properties", + "d.properties", + ) + doesNotContainEntries( + "a.properties", + ) + } + } + + @Issue( + "https://github.com/GradleUp/shadow/issues/717", + ) + @Test + fun shadowJarIsCachedCorrectlyWhenUsingIncludesExcludes() { + val mainClass = writeClass(className = "Main") + val main2Class = writeClass(className = "Main2") + projectScriptPath.appendText( + """ + dependencies { + implementation 'my:a:1.0' + implementation 'my:b:1.0' + } + """.trimIndent() + System.lineSeparator(), + ) + + assertCompositeExecutions { + containsEntries( + *entriesInAB, + mainClass, + main2Class, + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + exclude '**.properties' + } + """.trimIndent() + System.lineSeparator(), + ) + + assertCompositeExecutions { + containsEntries( + mainClass, + main2Class, + ) + doesNotContainEntries(*entriesInAB) + } + + projectScriptPath.appendText( + """ + $shadowJar { + include '$mainClass' + } + """.trimIndent() + System.lineSeparator(), + ) + + assertCompositeExecutions { + containsEntries( + mainClass, + ) + doesNotContainEntries( + main2Class, + "a.properties", + "a2.properties", + "b.properties", + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + include '$main2Class' + } + """.trimIndent() + System.lineSeparator(), + ) + + assertCompositeExecutions { + containsEntries( + mainClass, + main2Class, + ) + doesNotContainEntries(*entriesInAB) + } + } + + @Test + fun shadowJarIsCachedCorrectlyWhenUsingDependencyIncludesExcludes() { + val mainClass = writeClass(withImports = true) + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent() + System.lineSeparator(), + ) + + assertCompositeExecutions { + containsEntries( + mainClass, + *junitEntries, + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + dependencies { + exclude(dependency('junit:junit')) + } + } + """.trimIndent(), + ) + + assertCompositeExecutions { + containsEntries( + mainClass, + ) + doesNotContainEntries( + *junitEntries, + ) + } + } + + private fun dependOnAndExcludeArtifactD() { + projectScriptPath.appendText( + """ + dependencies { + implementation 'my:a:1.0' + implementation 'my:b:1.0' + implementation 'my:d:1.0' + } + $shadowJar { + dependencies { + exclude(dependency('my:d:1.0')) + } + } + """.trimIndent(), + ) + } + + private fun Assert.commonAssertions() { + containsEntries( + *entriesInAB, + "c.properties", + ) + doesNotContainEntries( + "d.properties", + ) + } +} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 6bf229a75..f107658c1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter -import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries import kotlin.io.path.appendText @@ -68,118 +67,6 @@ class ShadowJarCachingTest : BaseCachingTest() { } } - @Issue( - "https://github.com/GradleUp/shadow/issues/717", - ) - @Test - fun shadowJarIsCachedCorrectlyWhenUsingIncludesExcludes() { - val mainClass = writeClass(className = "Main") - val main2Class = writeClass(className = "Main2") - projectScriptPath.appendText( - """ - dependencies { - implementation 'my:a:1.0' - implementation 'my:b:1.0' - } - """.trimIndent() + System.lineSeparator(), - ) - - assertCompositeExecutions { - val entries = entriesInAB + arrayOf(mainClass, main2Class) - containsEntries(*entries) - } - - projectScriptPath.appendText( - """ - $shadowJar { - exclude '**.properties' - } - """.trimIndent() + System.lineSeparator(), - ) - - assertCompositeExecutions { - containsEntries( - mainClass, - main2Class, - ) - doesNotContainEntries(*entriesInAB) - } - - projectScriptPath.appendText( - """ - $shadowJar { - include '$mainClass' - } - """.trimIndent() + System.lineSeparator(), - ) - - assertCompositeExecutions { - containsEntries( - mainClass, - ) - doesNotContainEntries( - main2Class, - "a.properties", - "a2.properties", - "b.properties", - ) - } - - projectScriptPath.appendText( - """ - $shadowJar { - include '$main2Class' - } - """.trimIndent() + System.lineSeparator(), - ) - - assertCompositeExecutions { - containsEntries( - mainClass, - main2Class, - ) - doesNotContainEntries(*entriesInAB) - } - } - - @Test - fun shadowJarIsCachedCorrectlyWhenUsingDependencyIncludesExcludes() { - val mainClass = writeClass(withImports = true) - projectScriptPath.appendText( - """ - dependencies { - implementation 'junit:junit:3.8.2' - } - """.trimIndent() + System.lineSeparator(), - ) - - assertCompositeExecutions { - containsEntries( - mainClass, - *junitEntries, - ) - } - - projectScriptPath.appendText( - """ - $shadowJar { - dependencies { - exclude(dependency('junit:junit')) - } - } - """.trimIndent(), - ) - - assertCompositeExecutions { - containsEntries( - mainClass, - ) - doesNotContainEntries( - *junitEntries, - ) - } - } - @Test fun shadowJarIsCachedCorrectlyAfterDependencyFilterChanged() { publishArtifactCD() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index e0bb1c682..5acfdf58b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import assertk.all import assertk.assertThat import assertk.assertions.containsMatch import assertk.assertions.isEqualTo @@ -9,7 +8,6 @@ import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.writeText import org.gradle.api.file.DuplicatesStrategy -import org.gradle.testkit.runner.TaskOutcome.FAILED import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -206,10 +204,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { val result = runWithFailure(shadowJarTask) - assertThat(result).all { - taskOutcomeEquals(shadowJarTask, FAILED) - transform { it.output }.containsMatch(outputRegex.toRegex()) - } + assertThat(result.output).containsMatch(outputRegex.toRegex()) } @ParameterizedTest From e067276dc2b76564dff0ec3cd92e1acc4e61bb86 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 9 Mar 2025 20:15:53 -0400 Subject: [PATCH 324/941] Add a test for relocating all packages but certain one (#1325) * Test `relocateAllPackagesButSomeone` * Use ParameterizedTest * Cleanups * Rename --- .../gradle/plugins/shadow/RelocationTest.kt | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 99feaacf0..b7f84e2de 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -20,6 +20,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource import org.opentest4j.AssertionFailedError class RelocationTest : BasePluginTest() { @@ -467,6 +468,55 @@ class RelocationTest : BasePluginTest() { } } + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun relocateAllPackagesButCertainOne(exclude: Boolean) { + val relocateConfig = if (exclude) { + """ + exclude 'junit/**' + exclude 'META-INF/MANIFEST.MF' + """.trimIndent() + } else { + "" + } + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + relocate('', 'foo/') { + $relocateConfig + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + if (exclude) { + containsEntries( + "META-INF/MANIFEST.MF", + *junitEntries, + ) + doesNotContainEntries( + "foo/META-INF/MANIFEST.MF", + *junitEntries.map { "foo/$it" }.toTypedArray(), + ) + } else { + containsEntries( + "foo/META-INF/MANIFEST.MF", + *junitEntries.map { "foo/$it" }.toTypedArray(), + ) + doesNotContainEntries( + "META-INF/MANIFEST.MF", + *junitEntries, + ) + } + } + } + private companion object { @JvmStatic fun prefixProvider() = listOf( From a71acf4a3ddf7c8907679581bfe3ba81458e93f4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 10 Mar 2025 05:07:35 -0400 Subject: [PATCH 325/941] Double maxParallelForks (#1326) --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 1dec260cc..7a2efbc1c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -118,7 +118,7 @@ testing.suites { } targets.configureEach { testTask { - maxParallelForks = Runtime.getRuntime().availableProcessors() + maxParallelForks = Runtime.getRuntime().availableProcessors() * 2 } } } From 85f58bc48275f7087915a91c85e86ba861bb8e7f Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 11 Mar 2025 10:33:33 +0800 Subject: [PATCH 326/941] Revert "Double maxParallelForks (#1326)" This reverts commit a71acf4a3ddf7c8907679581bfe3ba81458e93f4. --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7a2efbc1c..1dec260cc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -118,7 +118,7 @@ testing.suites { } targets.configureEach { testTask { - maxParallelForks = Runtime.getRuntime().availableProcessors() * 2 + maxParallelForks = Runtime.getRuntime().availableProcessors() } } } From 2c0d047766a90620d13e9c1133058cb21ef53b18 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 10 Mar 2025 23:46:59 -0400 Subject: [PATCH 327/941] Support using type-safe dependency accessors in `ShadowJar.dependencies` (#1322) * Update dependency resolution for types * Update filterProjectDependencies * Update excludeDependency * Try to wrap accessors with `dependency` * Remove the usage of DependencyValueSource * Accept any for project * Update changelog * Note "Using type-safe dependency accessors" --- api/shadow.api | 1 + docs/changes/README.md | 1 + docs/configuration/dependencies/README.md | 38 +++++++++++++++++++ .../gradle/plugins/shadow/BasePluginTest.kt | 1 + .../gradle/plugins/shadow/FilteringTest.kt | 33 +++++++++++----- .../shadow/snippet/SnippetExecutable.kt | 7 +++- .../internal/AbstractDependencyFilter.kt | 19 +++++++++- .../plugins/shadow/tasks/DependencyFilter.kt | 5 +++ 8 files changed, 94 insertions(+), 11 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index ddb51769e..907cb3006 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -153,6 +153,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; public abstract fun exclude (Lorg/gradle/api/specs/Spec;)V public abstract fun include (Lorg/gradle/api/specs/Spec;)V + public abstract fun project (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; public abstract fun project (Ljava/lang/String;)Lorg/gradle/api/specs/Spec; public abstract fun project (Ljava/util/Map;)Lorg/gradle/api/specs/Spec; public abstract fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; diff --git a/docs/changes/README.md b/docs/changes/README.md index e26e172c2..226935ee1 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -6,6 +6,7 @@ **Added** - 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)) **Changed** diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 92bf039da..8b8441f57 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -219,6 +219,44 @@ This same pattern can be used for any of the dependency notation fields. } ``` +### Using type-safe dependency accessors + +You can also use type-safe project accessors or version catalog accessors to filter dependencies. + +=== "Kotlin" + + ```kotlin + dependencies { + // Have to declare this dependency in your libs.versions.toml + implementation(libs.log4j.core) + // Have to enable `TYPESAFE_PROJECT_ACCESSORS` flag in your settings.gradle.kts + implementation(projects.api) + } + tasks.shadowJar { + dependencies { + exclude(dependency(libs.log4j.core)) + exclude(project(projects.api)) + } + } + ``` + +=== "Groovy" + + ```groovy + dependencies { + // Have to declare this dependency in your libs.versions.toml + implementation libs.log4j.core + // Have to enable `TYPESAFE_PROJECT_ACCESSORS` flag in your settings.gradle + implementation projects.api + } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + dependencies { + exclude(dependency(libs.log4j.core)) + exclude(project(projects.api)) + } + } + ``` + ### Programmatically Selecting Dependencies to Filter If more complex decisions are needed to select the dependencies to be included, the 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 8a1b285dd..3128c0fd4 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 @@ -141,6 +141,7 @@ abstract class BasePluginTest { buildCache { $buildCacheBlock } + enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS' $endBlock """.trimIndent() + System.lineSeparator() } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 0863939d5..ca30840cf 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -8,6 +8,8 @@ import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeAll 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 FilteringTest : BasePluginTest() { @BeforeAll @@ -60,9 +62,10 @@ class FilteringTest : BasePluginTest() { } } - @Test - fun excludeDependency() { - dependOnAndExcludeArtifactD() + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun excludeDependency(useAccessor: Boolean) { + dependOnAndExcludeArtifactD(useAccessor) run(shadowJarTask) @@ -124,12 +127,14 @@ class FilteringTest : BasePluginTest() { } } - @Test - fun filterProjectDependencies() { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun filterProjectDependencies(useAccessor: Boolean) { + val clientProject = if (useAccessor) "project(projects.client)" else "project(':client')" writeClientAndServerModules( serverShadowBlock = """ dependencies { - exclude(project(':client')) + exclude($clientProject) } """.trimIndent(), ) @@ -205,15 +210,25 @@ class FilteringTest : BasePluginTest() { commonAssertions() } - private fun dependOnAndExcludeArtifactD() { + private fun dependOnAndExcludeArtifactD(useAccessor: Boolean = false) { + settingsScriptPath.appendText( + """ + dependencyResolutionManagement { + versionCatalogs.create('libs') { + library('my-d', 'my:d:1.0') + } + } + """.trimIndent(), + ) + val dependency = if (useAccessor) "libs.my.d" else "'my:d:1.0'" projectScriptPath.appendText( """ dependencies { - implementation 'my:d:1.0' + implementation $dependency } $shadowJar { dependencies { - exclude(dependency('my:d:1.0')) + exclude(dependency($dependency)) } } """.trimIndent(), diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index 311f1305e..17130c574 100644 --- a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -34,8 +34,13 @@ sealed class SnippetExecutable : Executable { mavenLocal() mavenCentral() } + versionCatalogs.create('libs') { + library('log4j-core', 'org.apache.logging.log4j:log4j-core:2.11.1') + } } - include 'api', 'main' + include ':api', ':main' + rootProject.name = 'snippet' + enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS' """.trimIndent(), ) projectRoot.addSubProject("api", pluginsBlock) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt index 6709c625c..a81189fef 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -4,9 +4,11 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.ResolvedArtifact import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Provider import org.gradle.api.specs.Spec internal sealed class AbstractDependencyFilter( @@ -43,6 +45,17 @@ internal sealed class AbstractDependencyFilter( includeSpecs.add(spec) } + override fun project(notation: Any): Spec { + @Suppress("UNCHECKED_CAST") + return when (notation) { + is ProjectDependency -> dependency(notation) + is Provider<*> -> project(notation.get() as String) + is String -> project(notation) + is Map<*, *> -> project(notation as Map) + else -> error("Unsupported notation type: ${notation::class.java}") + } + } + override fun project(notation: Map): Spec { return dependency(project.dependencies.project(notation)) } @@ -52,7 +65,11 @@ internal sealed class AbstractDependencyFilter( } override fun dependency(dependencyNotation: Any): Spec { - return dependency(project.dependencies.create(dependencyNotation)) + val realNotation = when (dependencyNotation) { + is Provider<*> -> dependencyNotation.get() + else -> dependencyNotation + } + return dependency(project.dependencies.create(realNotation)) } override fun dependency(dependency: Dependency): Spec { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index 90d3a2d82..cf74e3b23 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -29,6 +29,11 @@ public interface DependencyFilter : Serializable { */ public fun include(spec: Spec) + /** + * Create a [Spec] that matches the provided project [notation]. + */ + public fun project(notation: Any): Spec + /** * Create a [Spec] that matches the provided project [notation]. */ From f1ab294f4cf235b602505d48c695638922a86f04 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 11 Mar 2025 00:01:12 -0400 Subject: [PATCH 328/941] Rename integrationTest to documentTest (#1329) --- build.gradle.kts | 4 ++-- .../jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt | 0 .../gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt | 0 .../github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt | 0 .../gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt | 0 .../gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt | 0 .../gradle/plugins/shadow/snippet/SnippetExecutable.kt | 0 7 files changed, 2 insertions(+), 2 deletions(-) rename src/{integrationTest => documentationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt (100%) rename src/{integrationTest => documentationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt (100%) rename src/{integrationTest => documentationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt (100%) rename src/{integrationTest => documentationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt (100%) rename src/{integrationTest => documentationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt (100%) rename src/{integrationTest => documentationTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt (100%) diff --git a/build.gradle.kts b/build.gradle.kts index 1dec260cc..508d41574 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -73,7 +73,7 @@ testing.suites { implementation(libs.xmlunit) } } - register("integrationTest") { + register("documentTest") { targets.configureEach { testTask { val docsDir = file("docs") @@ -136,7 +136,7 @@ kotlin.target.compilations { gradlePlugin { testSourceSets( sourceSets["functionalTest"], - sourceSets["integrationTest"], + sourceSets["documentTest"], ) } diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt b/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt rename to src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt b/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt rename to src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt b/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt rename to src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt b/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt rename to src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt b/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt rename to src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt diff --git a/src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt similarity index 100% rename from src/integrationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt rename to src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt From 0ceb3b3347c6b6c006c8a1a73ddab0c2a3bd7bb1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 11 Mar 2025 00:05:48 -0400 Subject: [PATCH 329/941] Update the section about "Embedding Jar Files Inside Your Shadow Jar" (#1330) --- docs/configuration/README.md | 8 ----- docs/configuration/dependencies/README.md | 40 ++++++++++++++++++++--- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 6e385102d..5ecba474b 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -159,10 +159,6 @@ method can be used to add extra files. ```kotlin tasks.shadowJar { - from("extra.jar") { - // Copy extra.jar file (without unzipping) into META-INF/ in the shadowed JAR. - into("META-INF") - } from("Foo") { // Copy Foo file into Bar/ in the shadowed JAR. into("Bar") @@ -174,10 +170,6 @@ method can be used to add extra files. ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - from('extra.jar') { - // Copy extra.jar file (without unzipping) into META-INF/ in the shadowed JAR. - into('META-INF') - } from('Foo') { // Copy Foo file into Bar/ in the shadowed JAR. into('Bar') diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 8b8441f57..627300b52 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -31,11 +31,41 @@ have the intended effect, as `configurations.compile` will try to delegate to th ## Embedding Jar Files Inside Your Shadow Jar -Because of the way that Gradle handles dependency configuration, from a plugin perspective, shadow is unable to -distinguish between a jar file configured as a dependency and a jar file included in the resource folder. This means -that any jar found in a resource directory will be merged into the shadow jar the same as any other dependency. If -your intention is to embed the jar inside, you must rename the jar as to not end with `.jar` before the shadow task -begins. +The `shadowJar` task is a subclass of the `Jar` task, which means that the +[from](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20groovy.lang.Closure)) +method can be used to add extra files. + +=== "Kotlin" + + ```kotlin + dependencies { + // Merge foo.jar (with unzipping) into the shadowed JAR. + implementation(files("foo.jar")) + } + tasks.shadowJar { + from("bar.jar") { + // Copy bar.jar file (without unzipping) into META-INF/ in the shadowed JAR. + into("META-INF") + } + } + ``` + +=== "Groovy" + + ```groovy + dependencies { + // Merge foo.jar (with unzipping) into the shadowed JAR. + implementation files('foo.jar') + } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + from('bar.jar') { + // Copy bar.jar file (without unzipping) into META-INF/ in the shadowed JAR. + into('META-INF') + } + } + ``` + +See also [Adding Extra Files](../README.md#adding-extra-files) ## Filtering Dependencies From 5037c63f85df21c2aa2b50922c5b9935c52a0850 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 11 Mar 2025 00:17:00 -0400 Subject: [PATCH 330/941] Fix documentTest dir (#1331) --- .../github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt | 0 .../gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt | 0 .../com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt | 0 .../gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt | 0 .../gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt | 0 .../jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename src/{documentationTest => documentTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt (100%) rename src/{documentationTest => documentTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt (100%) rename src/{documentationTest => documentTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt (100%) rename src/{documentationTest => documentTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt (100%) rename src/{documentationTest => documentTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt (100%) rename src/{documentationTest => documentTest}/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt (100%) diff --git a/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt similarity index 100% rename from src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt rename to src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt diff --git a/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt similarity index 100% rename from src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt rename to src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt diff --git a/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt similarity index 100% rename from src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt rename to src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt diff --git a/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt similarity index 100% rename from src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt rename to src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt diff --git a/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt similarity index 100% rename from src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt rename to src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt diff --git a/src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt similarity index 100% rename from src/documentationTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt rename to src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt From 8aa41ea085e6339d8c694f1a258ed06011bf5646 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 11 Mar 2025 05:04:02 -0400 Subject: [PATCH 331/941] With applicationExtension in ShadowApplicationPlugin (#1334) --- .../plugins/shadow/ShadowApplicationPlugin.kt | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index daf9d3e78..31aaa0164 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -34,7 +34,6 @@ public abstract class ShadowApplicationPlugin : Plugin { } protected open fun Project.addRunTask() { - val extension = applicationExtension tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) { task -> task.description = "Runs this project as a JVM application using the shadow jar" task.group = ApplicationPlugin.APPLICATION_GROUP @@ -43,9 +42,12 @@ public abstract class ShadowApplicationPlugin : Plugin { i.destinationDir.resolve("lib/${s.archiveFile.get().asFile.name}") } task.classpath(jarFile) - task.mainModule.set(extension.mainModule) - task.mainClass.set(extension.mainClass) - task.jvmArguments.convention(provider { extension.applicationDefaultJvmArgs }) + + with(applicationExtension) { + task.mainModule.set(mainModule) + task.mainClass.set(mainClass) + task.jvmArguments.convention(provider { applicationDefaultJvmArgs }) + } task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) task.javaLauncher.convention(javaToolchainService.launcherFor(javaPluginExtension.toolchain)) @@ -53,7 +55,6 @@ public abstract class ShadowApplicationPlugin : Plugin { } protected open fun Project.addCreateScriptsTask() { - val extension = applicationExtension tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { task -> task.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" task.group = ApplicationPlugin.APPLICATION_GROUP @@ -65,12 +66,16 @@ public abstract class ShadowApplicationPlugin : Plugin { resources.text.fromString(requireResourceAsText("$dir/windowsStartScript.txt")) task.classpath = files(tasks.shadowJar) - task.mainModule.set(extension.mainModule) - task.mainClass.set(extension.mainClass) - task.conventionMapping.map("applicationName", extension::getApplicationName) - task.conventionMapping.map("outputDir") { layout.buildDirectory.dir("scriptsShadow").get().asFile } - task.conventionMapping.map("executableDir", extension::getExecutableDir) - task.conventionMapping.map("defaultJvmOpts", extension::getApplicationDefaultJvmArgs) + + with(applicationExtension) { + task.mainModule.set(mainModule) + task.mainClass.set(mainClass) + task.conventionMapping.map("applicationName", ::getApplicationName) + task.conventionMapping.map("outputDir") { layout.buildDirectory.dir("scriptsShadow").get().asFile } + task.conventionMapping.map("executableDir", ::getExecutableDir) + task.conventionMapping.map("defaultJvmOpts", ::getApplicationDefaultJvmArgs) + } + task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) } } From ce7c14819206336140bd134673ea1a0966334fc5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 11 Mar 2025 05:49:58 -0400 Subject: [PATCH 332/941] Merge dependency and project overloads in DependencyFilter (#1328) They are merged to accept `Any`. --- api/shadow.api | 3 -- docs/changes/README.md | 4 ++ .../internal/AbstractDependencyFilter.kt | 39 ++++++++----------- .../plugins/shadow/tasks/DependencyFilter.kt | 16 -------- 4 files changed, 20 insertions(+), 42 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 907cb3006..eb5e4c6ce 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -150,12 +150,9 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter : java/io/Serializable { public abstract fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; - public abstract fun dependency (Lorg/gradle/api/artifacts/Dependency;)Lorg/gradle/api/specs/Spec; public abstract fun exclude (Lorg/gradle/api/specs/Spec;)V public abstract fun include (Lorg/gradle/api/specs/Spec;)V public abstract fun project (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; - public abstract fun project (Ljava/lang/String;)Lorg/gradle/api/specs/Spec; - public abstract fun project (Ljava/util/Map;)Lorg/gradle/api/specs/Spec; public abstract fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; public abstract fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; } diff --git a/docs/changes/README.md b/docs/changes/README.md index 226935ee1..fe4105901 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -20,6 +20,10 @@ - Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) +**Removed** + +- **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) + ## [v9.0.0-beta10] (2025-03-05) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt index a81189fef..9743ccbf4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt @@ -47,21 +47,14 @@ internal sealed class AbstractDependencyFilter( override fun project(notation: Any): Spec { @Suppress("UNCHECKED_CAST") - return when (notation) { - is ProjectDependency -> dependency(notation) - is Provider<*> -> project(notation.get() as String) - is String -> project(notation) - is Map<*, *> -> project(notation as Map) - else -> error("Unsupported notation type: ${notation::class.java}") + val realNotation = when (notation) { + is ProjectDependency -> return notation.toSpec() + is Provider<*> -> mapOf("path" to notation.get()) + is String -> mapOf("path" to notation) + is Map<*, *> -> notation as Map + else -> throw IllegalArgumentException("Unsupported notation type: ${notation::class.java}") } - } - - override fun project(notation: Map): Spec { - return dependency(project.dependencies.project(notation)) - } - - override fun project(path: String): Spec { - return project(mapOf("path" to path)) + return project.dependencies.project(realNotation).toSpec() } override fun dependency(dependencyNotation: Any): Spec { @@ -69,15 +62,7 @@ internal sealed class AbstractDependencyFilter( is Provider<*> -> dependencyNotation.get() else -> dependencyNotation } - return dependency(project.dependencies.create(realNotation)) - } - - override fun dependency(dependency: Dependency): Spec { - return Spec { resolvedDependency -> - (dependency.group == null || resolvedDependency.moduleGroup.matches(dependency.group!!.toRegex())) && - resolvedDependency.moduleName.matches(dependency.name.toRegex()) && - (dependency.version == null || resolvedDependency.moduleVersion.matches(dependency.version!!.toRegex())) - } + return project.dependencies.create(realNotation).toSpec() } protected fun ResolvedDependency.isIncluded(): Boolean { @@ -85,4 +70,12 @@ internal sealed class AbstractDependencyFilter( val exclude = excludeSpecs.isNotEmpty() && excludeSpecs.any { it.isSatisfiedBy(this) } return include && !exclude } + + private fun Dependency.toSpec(): Spec { + return Spec { resolvedDependency -> + (group == null || resolvedDependency.moduleGroup.matches(group!!.toRegex())) && + resolvedDependency.moduleName.matches(name.toRegex()) && + (version == null || resolvedDependency.moduleVersion.matches(version!!.toRegex())) + } + } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index cf74e3b23..d17f806ae 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import java.io.Serializable import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.file.FileCollection import org.gradle.api.specs.Spec @@ -34,23 +33,8 @@ public interface DependencyFilter : Serializable { */ public fun project(notation: Any): Spec - /** - * Create a [Spec] that matches the provided project [notation]. - */ - public fun project(notation: Map): Spec - - /** - * Create a [Spec] that matches the provided project [path]. - */ - public fun project(path: String): Spec - /** * Create a [Spec] that matches the provided [dependencyNotation]. */ public fun dependency(dependencyNotation: Any): Spec - - /** - * Create a [Spec] that matches the provided [dependency]. - */ - public fun dependency(dependency: Dependency): Spec } From 1a123ee46d7fa19283355d380a421305801bd690 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 12 Mar 2025 10:19:20 +0800 Subject: [PATCH 333/941] Divide blocks --- docs/configuration/dependencies/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 627300b52..488931ca5 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -42,6 +42,7 @@ method can be used to add extra files. // Merge foo.jar (with unzipping) into the shadowed JAR. implementation(files("foo.jar")) } + tasks.shadowJar { from("bar.jar") { // Copy bar.jar file (without unzipping) into META-INF/ in the shadowed JAR. @@ -57,6 +58,7 @@ method can be used to add extra files. // Merge foo.jar (with unzipping) into the shadowed JAR. implementation files('foo.jar') } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { from('bar.jar') { // Copy bar.jar file (without unzipping) into META-INF/ in the shadowed JAR. @@ -83,6 +85,7 @@ Gradle's `configurations` block. dependencies { implementation("org.apache.logging.log4j:log4j-core:2.11.1") } + tasks.shadowJar { dependencies { exclude(dependency("org.apache.logging.log4j:log4j-core:2.11.1")) @@ -96,6 +99,7 @@ Gradle's `configurations` block. dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('org.apache.logging.log4j:log4j-core:2.11.1')) @@ -109,6 +113,7 @@ Gradle's `configurations` block. dependencies { implementation(project(":api")) } + tasks.shadowJar { dependencies { exclude(dependency(":api")) @@ -122,6 +127,7 @@ Gradle's `configurations` block. dependencies { implementation project(':api') } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(project(':api')) @@ -145,6 +151,7 @@ using any of these individual fields. dependencies { implementation("org.apache.logging.log4j:log4j-core:2.11.1") } + tasks.shadowJar { dependencies { exclude(dependency("org.apache.logging.log4j:log4j-core:.*")) @@ -158,6 +165,7 @@ using any of these individual fields. dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('org.apache.logging.log4j:log4j-core:.*')) @@ -173,6 +181,7 @@ Any of the individual fields can be safely absent and will function as though a dependencies { implementation("org.apache.logging.log4j:log4j-core:2.11.1") } + tasks.shadowJar { dependencies { exclude(dependency(":org.apache.logging.log4j:log4j-core")) @@ -186,6 +195,7 @@ Any of the individual fields can be safely absent and will function as though a dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('org.apache.logging.log4j:log4j-core')) @@ -203,6 +213,7 @@ This same pattern can be used for any of the dependency notation fields. dependencies { implementation("org.apache.logging.log4j:log4j-core:2.11.1") } + tasks.shadowJar { dependencies { exclude(dependency(":log4j-core:2.11.1")) @@ -216,6 +227,7 @@ This same pattern can be used for any of the dependency notation fields. dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency(':log4j-core:2.11.1')) @@ -229,6 +241,7 @@ This same pattern can be used for any of the dependency notation fields. dependencies { implementation("org.apache.logging.log4j:log4j-core:2.11.1") } + tasks.shadowJar { dependencies { exclude(dependency("org.apache.logging.log4j:2.11.1")) @@ -242,6 +255,7 @@ This same pattern can be used for any of the dependency notation fields. dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency('org.apache.logging.log4j:2.11.1')) @@ -262,6 +276,7 @@ You can also use type-safe project accessors or version catalog accessors to fil // Have to enable `TYPESAFE_PROJECT_ACCESSORS` flag in your settings.gradle.kts implementation(projects.api) } + tasks.shadowJar { dependencies { exclude(dependency(libs.log4j.core)) @@ -279,6 +294,7 @@ You can also use type-safe project accessors or version catalog accessors to fil // Have to enable `TYPESAFE_PROJECT_ACCESSORS` flag in your settings.gradle implementation projects.api } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude(dependency(libs.log4j.core)) @@ -299,6 +315,7 @@ block provides a method that accepts a `Closure` for selecting dependencies. dependencies { implementation("org.apache.logging.log4j:log4j-core:2.11.1") } + tasks.shadowJar { dependencies { exclude { @@ -314,6 +331,7 @@ block provides a method that accepts a `Closure` for selecting dependencies. dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.11.1' } + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { dependencies { exclude { From d48c3fa35838d05b19890a75a8e2ff73241a388a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 11 Mar 2025 22:37:02 -0400 Subject: [PATCH 334/941] Test with Kotlin 2.1.20-RC2 (#1336) * Update 2.1.20-RC https://kotlinlang.org/docs/whatsnew-eap.html * Try RC2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index fcba8d048..2f428546c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.1.10" +kotlin = "2.1.20-RC2" moshi = "1.15.2" [libraries] From 6457e1bfd6baef80fb1cc3a8d0610de8b4d40ec2 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 12 Mar 2025 12:07:12 +0800 Subject: [PATCH 335/941] Rename mainClass to mainClassEntry --- .../plugins/shadow/ApplicationPluginTest.kt | 4 +-- .../gradle/plugins/shadow/JavaPluginTest.kt | 22 +++++++------- .../gradle/plugins/shadow/KmpPluginTest.kt | 4 +-- .../gradle/plugins/shadow/RelocationTest.kt | 14 ++++----- .../shadow/caching/FilteringCachingTest.kt | 30 +++++++++---------- .../shadow/caching/RelocationCachingTest.kt | 6 ++-- 6 files changed, 40 insertions(+), 40 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 60df6330a..d7063d5e8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -116,7 +116,7 @@ class ApplicationPluginTest : BasePluginTest() { ) @Test fun canOverrideMainClassAttrInManifestBlock() { - val main2Class = writeClass(className = "Main2") + val main2ClassEntry = writeClass(className = "Main2") prepare( projectBlock = """ shadowJar { @@ -137,7 +137,7 @@ class ApplicationPluginTest : BasePluginTest() { assertions(run(runShadowTask).output, "foo") commonAssertions( jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"), - entriesContained = entriesInA + arrayOf(mainClass, main2Class), + entriesContained = entriesInA + arrayOf(mainClass, main2ClassEntry), mainClassAttr = "my.Main2", ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 0bf8409df..96dc74f7e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -71,7 +71,7 @@ class JavaPluginTest : BasePluginTest() { disabledReason = "Gradle 8.3 doesn't support Java 21.", ) fun compatibleWithMinGradleVersion() { - val mainClass = writeClass(withImports = true) + val mainClassEntry = writeClass(withImports = true) projectScriptPath.appendText( """ dependencies { @@ -86,7 +86,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - mainClass, + mainClassEntry, *junitEntries, ) } @@ -503,7 +503,7 @@ class JavaPluginTest : BasePluginTest() { ) @Test fun canRegisterCustomShadowJarTask() { - val mainClass = writeClass(sourceSet = "test", withImports = true) + val mainClassEntry = writeClass(sourceSet = "test", withImports = true) val testShadowJarTask = "testShadowJar" projectScriptPath.appendText( """ @@ -527,7 +527,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(jarPath("build/libs/my-1.0-tests.jar")).useAll { containsEntries( - mainClass, + mainClassEntry, *junitEntries, ) getMainAttr(mainClassAttributeKey).isNotNull() @@ -565,7 +565,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun worksWithArchiveFileName() { - val mainClass = writeClass() + val mainClassEntry = writeClass() projectScriptPath.appendText( """ dependencies { @@ -581,7 +581,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(jarPath("build/libs/my-shadow.tar")).useAll { containsEntries( - mainClass, + mainClassEntry, *junitEntries, ) } @@ -618,7 +618,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun canAddExtraFilesIntoShadowJar() { - val mainClass = writeClass() + val mainClassEntry = writeClass() path("Foo").writeText("Foo") projectScriptPath.appendText( """ @@ -637,7 +637,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - mainClass, + mainClassEntry, "META-INF/a-1.0.jar", "Bar/Foo", ) @@ -656,7 +656,7 @@ class JavaPluginTest : BasePluginTest() { } assertThat(jarPath(unzipped.name)).useAll { containsEntries(*entriesInA) - doesNotContainEntries(mainClass) + doesNotContainEntries(mainClassEntry) } } @@ -668,7 +668,7 @@ class JavaPluginTest : BasePluginTest() { val fooJar = buildJar("foo.jar") { insert("module-info.class", "module myModuleName {}") } - val mainClass = writeClass() + val mainClassEntry = writeClass() writeClass(className = "module-info") { "module myModuleName {}" } @@ -690,7 +690,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - mainClass, + mainClassEntry, "module-info.class", ) getContent("module-info.class").all { 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..4b014c370 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,7 +21,7 @@ class KmpPluginTest : BasePluginTest() { @Test fun compatKmpJvmTarget() { - val mainClass = writeClass(sourceSet = "jvmMain", isJava = false) + val mainClassEntry = writeClass(sourceSet = "jvmMain", isJava = false) projectScriptPath.appendText( """ kotlin { @@ -46,7 +46,7 @@ class KmpPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - mainClass, + mainClassEntry, *entriesInAB, ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index b7f84e2de..36e4d0ff9 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -27,7 +27,7 @@ class RelocationTest : BasePluginTest() { @ParameterizedTest @MethodSource("prefixProvider") fun autoRelocation(relocationPrefix: String) { - val mainClass = writeClass() + val mainClassEntry = writeClass() projectScriptPath.appendText( """ dependencies { @@ -45,11 +45,11 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - mainClass, + mainClassEntry, *junitEntries.map { "$entryPrefix/$it" }.toTypedArray(), ) doesNotContainEntries( - "$entryPrefix/$mainClass", + "$entryPrefix/$mainClassEntry", *junitEntries, ) } @@ -64,7 +64,7 @@ class RelocationTest : BasePluginTest() { ) @Test fun relocateDependencyFiles() { - val mainClass = writeClass() + val mainClassEntry = writeClass() projectScriptPath.appendText( """ dependencies { @@ -90,7 +90,7 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - mainClass, + mainClassEntry, *runnerEntries, *frameworkEntries, *otherJunitEntries, @@ -105,7 +105,7 @@ class RelocationTest : BasePluginTest() { @Test fun relocateDependencyFilesWithFiltering() { - val mainClass = writeClass() + val mainClassEntry = writeClass() projectScriptPath.appendText( """ dependencies { @@ -135,7 +135,7 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsEntries( - mainClass, + mainClassEntry, *runnerEntries, *frameworkEntries, *otherJunitEntries, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt index df26e0ada..9034c271e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt @@ -71,8 +71,8 @@ class FilteringCachingTest : BaseCachingTest() { ) @Test fun shadowJarIsCachedCorrectlyWhenUsingIncludesExcludes() { - val mainClass = writeClass(className = "Main") - val main2Class = writeClass(className = "Main2") + val mainClassEntry = writeClass(className = "Main") + val main2ClassEntry = writeClass(className = "Main2") projectScriptPath.appendText( """ dependencies { @@ -85,8 +85,8 @@ class FilteringCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( *entriesInAB, - mainClass, - main2Class, + mainClassEntry, + main2ClassEntry, ) } @@ -100,8 +100,8 @@ class FilteringCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - mainClass, - main2Class, + mainClassEntry, + main2ClassEntry, ) doesNotContainEntries(*entriesInAB) } @@ -109,17 +109,17 @@ class FilteringCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - include '$mainClass' + include '$mainClassEntry' } """.trimIndent() + System.lineSeparator(), ) assertCompositeExecutions { containsEntries( - mainClass, + mainClassEntry, ) doesNotContainEntries( - main2Class, + main2ClassEntry, "a.properties", "a2.properties", "b.properties", @@ -129,15 +129,15 @@ class FilteringCachingTest : BaseCachingTest() { projectScriptPath.appendText( """ $shadowJar { - include '$main2Class' + include '$main2ClassEntry' } """.trimIndent() + System.lineSeparator(), ) assertCompositeExecutions { containsEntries( - mainClass, - main2Class, + mainClassEntry, + main2ClassEntry, ) doesNotContainEntries(*entriesInAB) } @@ -145,7 +145,7 @@ class FilteringCachingTest : BaseCachingTest() { @Test fun shadowJarIsCachedCorrectlyWhenUsingDependencyIncludesExcludes() { - val mainClass = writeClass(withImports = true) + val mainClassEntry = writeClass(withImports = true) projectScriptPath.appendText( """ dependencies { @@ -156,7 +156,7 @@ class FilteringCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - mainClass, + mainClassEntry, *junitEntries, ) } @@ -173,7 +173,7 @@ class FilteringCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - mainClass, + mainClassEntry, ) doesNotContainEntries( *junitEntries, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index 24e9af75e..1d0429484 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -18,11 +18,11 @@ class RelocationCachingTest : BaseCachingTest() { } """.trimIndent() + System.lineSeparator(), ) - val mainClass = writeClass(withImports = true) + val mainClassEntry = writeClass(withImports = true) assertCompositeExecutions { containsEntries( - mainClass, + mainClassEntry, *junitEntries, ) } @@ -39,7 +39,7 @@ class RelocationCachingTest : BaseCachingTest() { assertCompositeExecutions { containsEntries( - mainClass, + mainClassEntry, *shadowedEntries, ) doesNotContainEntries( From b5bcde9608eac9fe4839a98a8131d0a7c5375ad5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Mar 2025 02:05:52 -0400 Subject: [PATCH 336/941] 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 <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 1 + docs/kmp-plugin/README.md | 10 ++++- .../gradle/plugins/shadow/KmpPluginTest.kt | 43 +++++++++++++++++++ .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 26 ++++++++++- 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index fe4105901..702487abe 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,6 +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 for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) **Changed** diff --git a/docs/kmp-plugin/README.md b/docs/kmp-plugin/README.md index 8c7c682e2..3bcdbc106 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 available 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 available from Kotlin 2.1.0 + it.mainClass.set('myapp.MainKt') + } sourceSets { commonMain { dependencies { 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 4b014c370..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 @@ -1,11 +1,16 @@ 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 import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class KmpPluginTest : BasePluginTest() { @BeforeEach @@ -51,4 +56,42 @@ class KmpPluginTest : BasePluginTest() { ) } } + + @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 c0c443041..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 @@ -1,15 +1,21 @@ 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 +import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as KgpVersion 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 +24,24 @@ public abstract class ShadowKmpPlugin : Plugin { listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) }, ) + configureMainClass(task) + } + } + } + + private fun Project.configureMainClass(task: ShadowJar) { + if (KgpVersion.DEFAULT < KgpVersion.KOTLIN_2_1) return + + @OptIn(ExperimentalKotlinGradlePluginApi::class) + kmpExtension.jvm().mainRun { + // 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 { + val realClass = mainClassName.get().orNull + if (!task.manifest.attributes.contains(mainClassAttributeKey) && !realClass.isNullOrEmpty()) { + task.manifest.attributes[mainClassAttributeKey] = realClass + } } } } From a3e46babb793136a918a7d248ff372cc9a8d2d67 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Mar 2025 04:55:17 -0400 Subject: [PATCH 337/941] Show how to integrate with Groovy and Scala plugins (#1338) * Declare JvmName for writeClass for Kotlin * Replace isJava * Fix file suffixes * Add GroovyPluginTest * Add ScalaPluginTest * Add docs --- docs/groovy-and-scala-plugins/README.md | 91 +++++++++++++++++++ mkdocs.yml | 1 + .../gradle/plugins/shadow/BasePluginTest.kt | 91 +++++++++++-------- .../gradle/plugins/shadow/GroovyPluginTest.kt | 44 +++++++++ .../gradle/plugins/shadow/KmpPluginTest.kt | 7 +- .../gradle/plugins/shadow/ScalaPluginTest.kt | 44 +++++++++ .../gradle/plugins/shadow/util/JvmLang.kt | 13 +++ 7 files changed, 252 insertions(+), 39 deletions(-) create mode 100644 docs/groovy-and-scala-plugins/README.md create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt diff --git a/docs/groovy-and-scala-plugins/README.md b/docs/groovy-and-scala-plugins/README.md new file mode 100644 index 000000000..e9979d50d --- /dev/null +++ b/docs/groovy-and-scala-plugins/README.md @@ -0,0 +1,91 @@ +# Integrating with Groovy and Scala Plugins + +Shadow also works well for Groovy and Scala, here are integration examples: + +For Groovy: + +=== "Kotlin" + + ```kotlin + plugins { + groovy + id("com.gradleup.shadow") + } + + dependencies { + // If you don't want the Groovy standard library to be shadowed, please replace `implementation` with `api`. + implementation(localGroovy()) + } + + tasks.shadowJar { + manifest { + // Optionally, set the main class for the shadowed JAR. + attributes["Main-Class"] = "com.example.Main" + } + } + ``` + +=== "Groovy" + + ```groovy + plugins { + id 'groovy' + id 'com.gradleup.shadow' + } + + dependencies { + // If you don't want the Groovy standard library to be shadowed, please replace `implementation` with `api`. + implementation localGroovy() + } + + 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.Main' + } + } + ``` + +For Scala: + +=== "Kotlin" + + ```kotlin + plugins { + scala + id("com.gradleup.shadow") + } + + dependencies { + // If you don't want the Scala standard library to be shadowed, please replace `implementation` with `api`. + implementation("org.scala-lang:scala-library:2.13.16") + } + + tasks.shadowJar { + manifest { + // Optionally, set the main class for the shadowed JAR. + attributes["Main-Class"] = "com.example.Main" + } + } + ``` + +=== "Groovy" + + ```groovy + plugins { + id 'scala' + id 'com.gradleup.shadow' + } + + dependencies { + // If you don't want the Scala standard library to be shadowed, please replace `implementation` with `api`. + implementation 'org.scala-lang:scala-library:2.13.16' + } + + 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.Main' + } + } + ``` diff --git a/mkdocs.yml b/mkdocs.yml index 073e93647..a2bbb2e34 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -71,6 +71,7 @@ nav: - 'Custom Tasks': custom-tasks/README.md - 'Application Plugin': application-plugin/README.md - 'KMP Plugin': kmp-plugin/README.md + - 'Groovy and Scala Plugins': groovy-and-scala-plugins/README.md - 'Publishing': publishing/README.md - 'Multi-Project': multi-project/README.md - 'Plugins': plugins/README.md 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 3128c0fd4..555a84b1c 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 @@ -14,6 +14,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransform import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder import com.github.jengelman.gradle.plugins.shadow.util.JarPath +import com.github.jengelman.gradle.plugins.shadow.util.JvmLang import java.io.Closeable import java.nio.file.Path import java.util.Properties @@ -202,46 +203,64 @@ abstract class BasePluginTest { packageName: String = "my", withImports: Boolean = false, className: String = "Main", - isJava: Boolean = true, - classContent: () -> String = { - if (isJava) { - val imports = if (withImports) "import junit.framework.Test;" else "" - val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" - """ - 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); + jvmLang: JvmLang = JvmLang.Java, + content: () -> String = { + when (jvmLang) { + JvmLang.Groovy, + JvmLang.Java, + -> { + val imports = if (withImports) "import junit.framework.Test;" else "" + val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" + """ + 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); + } } - } - """.trimIndent() - } else { - val imports = if (withImports) "import junit.framework.Test;" else "" - val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" - """ - 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() + """.trimIndent() + } + JvmLang.Kotlin -> { + val imports = if (withImports) "import junit.framework.Test;" else "" + val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" + """ + @file:JvmName("$className") + 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() + } + JvmLang.Scala -> { + val imports = if (withImports) "import junit.framework.Test" else "" + val classRef = if (withImports) "\"Refs: \" + classOf[Test].getName" else "\"Refs: null\"" + """ + package $packageName + $imports + object $className { + def main(args: Array[String]): Unit = { + if (args.isEmpty) throw new IllegalArgumentException("No arguments provided.") + val content = s"Hello, World! (%s) from $className".format(args: _*) + println(content) + println($classRef) + } + } + """.trimIndent() + } } }, ): String { - if (isJava) { - path("src/$sourceSet/java/$packageName/$className.java").writeText(classContent()) - } else { - path("src/$sourceSet/kotlin/$packageName/$className.kt").writeText(classContent()) - } - val baseClassPath = packageName.replace('.', '/') + "/$className" - return if (isJava) "$baseClassPath.class" else "${baseClassPath}Kt.class" + val basePath = packageName.replace('.', '/') + "/$className" + path("src/$sourceSet/$jvmLang/$basePath.${jvmLang.suffix}").writeText(content()) + return "$basePath.class" } fun writeClientAndServerModules( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt new file mode 100644 index 000000000..132990442 --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt @@ -0,0 +1,44 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.util.JvmLang +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 GroovyPluginTest : BasePluginTest() { + @BeforeEach + override fun setup() { + super.setup() + val projectBuildScript = getDefaultProjectBuildScript( + plugin = "groovy", + withGroup = true, + withVersion = true, + ) + projectScriptPath.writeText(projectBuildScript) + } + + @Test + fun compatGroovy() { + val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Groovy) + projectScriptPath.appendText( + """ + dependencies { + compileOnly localGroovy() + implementation 'junit:junit:3.8.2' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries( + mainClassEntry, + *junitEntries, + ) + } + } +} 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 f00924258..200f2ed37 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,6 +3,7 @@ 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.JvmLang import com.github.jengelman.gradle.plugins.shadow.util.containsEntries import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import kotlin.io.path.appendText @@ -26,7 +27,7 @@ class KmpPluginTest : BasePluginTest() { @Test fun compatKmpJvmTarget() { - val mainClassEntry = writeClass(sourceSet = "jvmMain", isJava = false) + val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin) projectScriptPath.appendText( """ kotlin { @@ -60,8 +61,8 @@ class KmpPluginTest : BasePluginTest() { @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 mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin) + val main2ClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin, className = "Main2") val mainClassName = "my.Main" val main2ClassName = "my.Main2" val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt new file mode 100644 index 000000000..4c3a14a17 --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt @@ -0,0 +1,44 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.util.JvmLang +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 ScalaPluginTest : BasePluginTest() { + @BeforeEach + override fun setup() { + super.setup() + val projectBuildScript = getDefaultProjectBuildScript( + plugin = "scala", + withGroup = true, + withVersion = true, + ) + projectScriptPath.writeText(projectBuildScript) + } + + @Test + fun compatScala() { + val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Scala) + projectScriptPath.appendText( + """ + dependencies { + compileOnly 'org.scala-lang:scala-library:2.13.16' + implementation 'junit:junit:3.8.2' + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries( + mainClassEntry, + *junitEntries, + ) + } + } +} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt new file mode 100644 index 000000000..3cc550d7d --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt @@ -0,0 +1,13 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +enum class JvmLang( + val suffix: String, +) { + Groovy("groovy"), + Java("java"), + Kotlin("kt"), + Scala("scala"), + ; + + override fun toString(): String = name.lowercase() +} From a6aff560f6e45b570ad16d1fa3880686f117a0d2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Mar 2025 06:09:55 -0400 Subject: [PATCH 338/941] Document Kotlin JVM plugin (#1339) * Rename files * Mention things about Kotlin JVM plugin * Fix kt writing * Note stdlib things * Fix typo * Remove duplicates --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/{kmp-plugin => kotlin-plugins}/README.md | 51 +++++++++++++++++-- gradle.properties | 2 +- mkdocs.yml | 2 +- .../gradle/plugins/shadow/BasePluginTest.kt | 4 +- ...{KmpPluginTest.kt => KotlinPluginsTest.kt} | 24 ++++++++- 5 files changed, 75 insertions(+), 8 deletions(-) rename docs/{kmp-plugin => kotlin-plugins}/README.md (54%) rename src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{KmpPluginTest.kt => KotlinPluginsTest.kt} (82%) diff --git a/docs/kmp-plugin/README.md b/docs/kotlin-plugins/README.md similarity index 54% rename from docs/kmp-plugin/README.md rename to docs/kotlin-plugins/README.md index 3bcdbc106..aa535eb92 100644 --- a/docs/kmp-plugin/README.md +++ b/docs/kotlin-plugins/README.md @@ -1,4 +1,49 @@ -# Integrating with Kotlin Multiplatform Plugin +# Integrating with Kotlin Plugins + +Kotlin standard libraries (stdlib) are added by Kotlin plugins by default, they will be bundled into the shadowed JARs automatically. +If you don't need a standard library at all, you can add the following Gradle property to your gradle.properties file: + +```properties +kotlin.stdlib.default.dependency=false +``` + +See more information about [Dependency on the standard library](https://kotlinlang.org/docs/gradle-configure-project.html#dependency-on-the-standard-library). + +## For Kotlin JVM Plugin + +Shadow works well for Kotlin JVM projects like Java projects. Here is an example: + +=== "Kotlin" + + ```kotlin + plugins { + kotlin("jvm") + id("com.gradleup.shadow") + } + + dependencies { + implementation("io.ktor:ktor-client-okhttp:3.1.0") + } + ``` + +=== "Groovy" + + ```groovy + plugins { + id 'org.jetbrains.kotlin.jvm' + id 'com.gradleup.shadow' + } + + dependencies { + implementation 'io.ktor:ktor-client-okhttp:3.1.0' + } + ``` + +You can mix the Kotlin JVM plugin with `java-gradle-plugin`, `application`, and other Java plugins, +easily organize your build logic for [Packaging Gradle Plugins](../plugins/README.md), [Publishing Libraries](../publishing/README.md), +[Running Applications](../application-plugin/README.md), and so on. + +## For Kotlin Multiplatform Plugin Shadow honors Kotlin's [`org.jetbrains.kotlin.multiplatform`](https://kotlinlang.org/docs/multiplatform-intro.html) plugin and will automatically @@ -59,12 +104,12 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. sourceSets { commonMain { dependencies { - implementation "io.ktor:ktor-client-core:$ktorVersion" + implementation 'io.ktor:ktor-client-core:$ktorVersion' } } jvmMain { dependencies { - implementation "io.ktor:ktor-client-okhttp:$ktorVersion" + implementation 'io.ktor:ktor-client-okhttp:$ktorVersion' } } } diff --git a/gradle.properties b/gradle.properties index 69c60af1a..962bd3557 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # Omit automatic compile dependency on kotlin-stdlib. -# https://kotlinlang.org/docs/gradle.html#dependency-on-the-standard-library +# https://kotlinlang.org/docs/gradle-configure-project.html#dependency-on-the-standard-library kotlin.stdlib.default.dependency=false org.gradle.caching=true diff --git a/mkdocs.yml b/mkdocs.yml index a2bbb2e34..68123a736 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -70,7 +70,7 @@ nav: - 'Reproducible Builds': configuration/reproducible-builds/README.md - 'Custom Tasks': custom-tasks/README.md - 'Application Plugin': application-plugin/README.md - - 'KMP Plugin': kmp-plugin/README.md + - 'Kotlin Plugins': kotlin-plugins/README.md - 'Groovy and Scala Plugins': groovy-and-scala-plugins/README.md - 'Publishing': publishing/README.md - 'Multi-Project': multi-project/README.md 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 555a84b1c..aaf7adc0b 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 @@ -225,8 +225,8 @@ abstract class BasePluginTest { """.trimIndent() } JvmLang.Kotlin -> { - val imports = if (withImports) "import junit.framework.Test;" else "" - val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" + val imports = if (withImports) "import junit.framework.Test" else "" + val classRef = if (withImports) "\"Refs: \" + Test::class.java.name" else "\"Refs: null\"" """ @file:JvmName("$className") package $packageName diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt similarity index 82% rename from src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 200f2ed37..fefb05451 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KmpPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -class KmpPluginTest : BasePluginTest() { +class KotlinPluginsTest : BasePluginTest() { @BeforeEach override fun setup() { super.setup() @@ -25,6 +25,28 @@ class KmpPluginTest : BasePluginTest() { projectScriptPath.writeText(projectBuildScript) } + @Test + fun compatKotlinJvmPlugin() { + projectScriptPath.writeText( + """ + ${getDefaultProjectBuildScript(plugin = "org.jetbrains.kotlin.jvm", withGroup = true, withVersion = true)} + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent(), + ) + val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Kotlin) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries( + mainClassEntry, + *junitEntries, + ) + } + } + @Test fun compatKmpJvmTarget() { val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin) From dbbceb94a7ca15daf47471b3649bc8284112f324 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Mar 2025 07:06:14 -0400 Subject: [PATCH 339/941] Reorganize doc navigations (#1340) * Remove "Shadowing Gradle Plugins" * Update "Default Java/Kotlin/Groovy Tasks" * Update Gradle Plugins nav * Update docs/getting-started/README.md Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> * Merge introductions * Move introduction/README.md out * Compat image for GMF * Note "I" * Add myself into "Maintainers" --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs/README.md | 63 ++++++++++++++++++---- docs/about/README.md | 5 +- docs/getting-started/README.md | 16 +++--- docs/{plugins => gradle-plugins}/README.md | 0 docs/introduction/README.md | 43 --------------- docs/kotlin-plugins/README.md | 2 +- mkdocs.yml | 5 +- 7 files changed, 65 insertions(+), 69 deletions(-) rename docs/{plugins => gradle-plugins}/README.md (100%) delete mode 100644 docs/introduction/README.md diff --git a/docs/README.md b/docs/README.md index 6cd9f05ec..15f729b08 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,16 +1,59 @@
- Shadow Gradle Plugin + Shadow Gradle Plugin

Shadow Gradle Plugin

-

The library author's dependency toolkit

---- +# Introduction -Previously this plugin was developed by [@johnrengelman](https://github.com/johnrengelman) and published under the ID [ -`com.github.johnrengelman.shadow`](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) -before maintenance was transferred to the [GradleUp organization](https://github.com/GradleUp) to ensure future -development, see [#908](https://github.com/GradleUp/shadow/issues/908). +Shadow is a Gradle plugin for combining a project's dependency classes and resources into a single +output Jar. +The combined Jar is often referred to a _fat-jar_ or _uber-jar_. +Shadow utilizes [`JarInputStream`](https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarInputStream.html) and [`JarOutputStream`](https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarOutputStream.html) to efficiently process dependent libraries +into the output jar without incurring the I/O overhead of expanding the jars to disk. -If you are still using the old plugin ID in your build script, we recommend to switch to the new plugin ID [ -`com.gradleup.shadow`](https://plugins.gradle.org/plugin/com.gradleup.shadow) -and update to the latest version to receive all the latest bug fixes and improvements. +!!! warning "Plugin ID Change" + + Previously this plugin was developed by [@johnrengelman](https://github.com/johnrengelman) and published under the ID [ + `com.github.johnrengelman.shadow`](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) + before maintenance was transferred to the [GradleUp organization](https://github.com/GradleUp) to ensure future + development, see [#908](https://github.com/GradleUp/shadow/issues/908). + + If you are still using the old plugin ID in your build script, we recommend to switch to the new plugin ID [ + `com.gradleup.shadow`](https://plugins.gradle.org/plugin/com.gradleup.shadow) + and update to the latest version to receive all the latest bug fixes and improvements. + +## Benefits of Shadow + +Shadowing a project output has 2 major use cases: + +1. Creating an _executable_ JAR distribution +2. Bundling and relocating common dependencies in libraries to avoid classpath conflicts + +### Executable Distributions + +Executable distribution is the main use case for deploying an _application_ that can be executed/run in the runtime +environment. +In the case of Shadow, this is a single _uber_ or _fat_ JAR. +The JAR file contains all the application code and dependent libraries to execute (not including the standard JVM +libraries). +The shadow JAR does **not** include the JRE itself. +It must be available on the target system. + +Executable JARs contain a JAR MANIFEST that specifies the application Main Class. +This allows the application to be started with a single command: + +```shell +java -jar application-shadow.jar +``` + +### Library Bundling + +Dependency bundling and relocation is the main use case for _library_ authors. +The goal of a bundled library is to create a pre-packaged dependency for other libraries or applications to utilize. +Often in these scenarios, a library may contain a dependency that a downstream library or application also uses. +In _some_ cases, different versions of this common dependency can cause an issue in either the upstream library or +the downstream application. +These issues often manifest themselves as binary incompatibilities in either the library or application code. + +By utilizing Shadow's ability to _relocate_ the package names for dependencies, a library author can ensure that the +library's dependencies will not conflict with the same dependency being declared by the downstream application. diff --git a/docs/about/README.md b/docs/about/README.md index 749eb959f..898226a76 100644 --- a/docs/about/README.md +++ b/docs/about/README.md @@ -1,13 +1,13 @@ # About This Project -I started this project in December 2012. We were working on converting from a monolithic application into the +I (John Engelman) started this project in December 2012. We were working on converting from a monolithic application into the new hot jazz of "microservices" using Dropwizard. I had also just started learning about Gradle and I knew that the incremental build system it provided would benefit our development team greatly. Unfortunately, the closest thing that Gradle had to Maven's Shade plugin was its ability to create application TARs and ZIPs. -So, Charlie Knudsen and I (John Engelman) set out to port the existing Shade code into a Gradle plugin. +So, Charlie Knudsen and I set out to port the existing Shade code into a Gradle plugin. This port is what existed up until the `0.9` milestone releases for Shadow. It functioned, but it wasn't idiomatic Gradle by any means. @@ -19,6 +19,7 @@ so Shadow was published there. ## Maintainers * [John Engelman](https://github.com/johnrengelman) +* [Goooler](https://github.com/Goooler) ## Contributors diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 1d4806f66..b9dbd48ce 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -116,13 +116,16 @@ Instead, Shadow _reacts_ This means, that for most users, the `java` or `groovy` plugins must be _explicitly_ applied to have the desired effect. -## Default Java/Groovy Tasks +## Default Java/Kotlin/Groovy Tasks -In the presence of the `java` or `groovy` plugins, Shadow will automatically configure the -following behavior: +In the presence of the `java`, `org.jetbrains.kotlin.jvm` or `groovy` plugins +(that apply [JavaPlugin](https://docs.gradle.org/current/userguide/java_plugin.html) in their build logic), +Shadow will automatically configure the following behavior: * Adds a `shadowJar` task to the project. * Adds a `shadow` configuration to the project. +* Adds a `shadow` variant to the project. +* Adds a `shadow` component to the project. * Configures the `shadowJar` task to include all sources from the project's `main` sourceSet. * Configures the `shadowJar` task to bundle all dependencies from the `runtimeClasspath` configuration. * Configures the _classifier_ attribute of the `shadowJar` task to be `'all'` . @@ -137,10 +140,3 @@ following behavior: * `META-INF/versions/**/module-info.class` * `module-info.class` * Creates and registers the `shadow` component in the project (used for integrating with `maven-publish`). - -## Shadowing Gradle Plugins - -Shadow ships with a companion task that can be used to automatically discover dependency packages and configure -them for relocation. This is useful in projects if you want to relocate all dependencies. - -For more details see the section [Using Shadow to Package Gradle Plugins](../plugins/README.md) diff --git a/docs/plugins/README.md b/docs/gradle-plugins/README.md similarity index 100% rename from docs/plugins/README.md rename to docs/gradle-plugins/README.md diff --git a/docs/introduction/README.md b/docs/introduction/README.md deleted file mode 100644 index dbdf4ab3f..000000000 --- a/docs/introduction/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Introduction - -Shadow is a Gradle plugin for combining a project's dependency classes and resources into a single -output Jar. -The combined Jar is often referred to a _fat-jar_ or _uber-jar_. -Shadow utilizes [`JarInputStream`](https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarInputStream.html) and [`JarOutputStream`](https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarOutputStream.html) to efficiently process dependent libraries -into the output jar without incurring the I/O overhead of expanding the jars to disk. - -## Benefits of Shadow - -Shadowing a project output has 2 major use cases: - -1. Creating an _executable_ JAR distribution -2. Bundling and relocating common dependencies in libraries to avoid classpath conflicts - -### Executable Distributions - -Executable distribution is the main use case for deploying an _application_ that can be executed/run in the runtime -environment. -In the case of Shadow, this is a single _uber_ or _fat_ JAR. -The JAR file contains all the application code and dependent libraries to execute (not including the standard JVM -libraries). -The shadow JAR does **not** include the JRE itself. -It must be available on the target system. - -Executable JARs contain a JAR MANIFEST that specifies the application Main Class. -This allows the application to be started with a single command: - -```shell -java -jar application-shadow.jar -``` - -### Library Bundling - -Dependency bundling and relocation is the main use case for _library_ authors. -The goal of a bundled library is to create a pre-packaged dependency for other libraries or applications to utilize. -Often in these scenarios, a library may contain a dependency that a downstream library or application also uses. -In _some_ cases, different versions of this common dependency can cause an issue in either the upstream library or -the downstream application. -These issues often manifest themselves as binary incompatibilities in either the library or application code. - -By utilizing Shadow's ability to _relocate_ the package names for dependencies, a library author can ensure that the -library's dependencies will not conflict with the same dependency being declared by the downstream application. diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index aa535eb92..4248ba8e4 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -40,7 +40,7 @@ Shadow works well for Kotlin JVM projects like Java projects. Here is an example ``` You can mix the Kotlin JVM plugin with `java-gradle-plugin`, `application`, and other Java plugins, -easily organize your build logic for [Packaging Gradle Plugins](../plugins/README.md), [Publishing Libraries](../publishing/README.md), +easily organize your build logic for [Packaging Gradle Plugins](../gradle-plugins/README.md), [Publishing Libraries](../publishing/README.md), [Running Applications](../application-plugin/README.md), and so on. ## For Kotlin Multiplatform Plugin diff --git a/mkdocs.yml b/mkdocs.yml index 68123a736..0ac3bca62 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -57,8 +57,7 @@ markdown_extensions: - md_in_html nav: - - 'Home': README.md - - 'Introduction': introduction/README.md + - 'Introduction': README.md - 'Getting Started': getting-started/README.md - 'Configuration': - 'Overview': configuration/README.md @@ -74,7 +73,7 @@ nav: - 'Groovy and Scala Plugins': groovy-and-scala-plugins/README.md - 'Publishing': publishing/README.md - 'Multi-Project': multi-project/README.md - - 'Plugins': plugins/README.md + - 'Gradle Plugins': gradle-plugins/README.md - 'Changes': changes/README.md - 'API': api/index.html - 'About': about/README.md From 84acecdcc7232468a822f3bd6b95d7044be3c667 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Mar 2025 11:23:34 -0400 Subject: [PATCH 340/941] Document relocate only trick (#1341) * Document relocating project resources only * Test `relocateProjectResourcesOnly` --- docs/configuration/relocation/README.md | 28 +++++++++++++++++ .../gradle/plugins/shadow/RelocationTest.kt | 30 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 333e9cc5a..8dee715e2 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -127,3 +127,31 @@ In versions before 8.1.0 it was necessary to configure a separate `ConfigureShad in the configurations declared to be shadowed. By default, this is the `runtime` or `runtimeClasspath` configurations. Be mindful that some Gradle plugins will automatically add dependencies to your class path. You may need to remove these dependencies if you do not intend to shadow them into your library. + +## Relocating Project Resources Only + +If you want to relocate the resources of the project only and exclude all dependencies (related to a normal JAR but with +relocating), you can try out the trick like: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + // Empty configurations list will exclude all dependencies. + configurations = emptyList() + relocate("com.example", "shadow.com.example") + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Empty configurations list will exclude all dependencies. + configurations = [] + relocate 'com.example', 'shadow.com.example' + } + ``` + +This is useful in some cases like [#759](https://github.com/GradleUp/shadow/issues/759) mentioned. See +[Configuring Shadowed Dependencies](../dependencies/README.md) for more information about `configurations`. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 36e4d0ff9..8bff7997c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -517,6 +517,36 @@ class RelocationTest : BasePluginTest() { } } + @Test + fun relocateProjectResourcesOnly() { + val mainClassEntry = writeClass() + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJar { + configurations = [] + relocate('', 'foo/') + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsEntries( + "foo/$mainClassEntry", + "foo/META-INF/MANIFEST.MF", + ) + doesNotContainEntries( + "META-INF/MANIFEST.MF", + *junitEntries, + *junitEntries.map { "foo/$it" }.toTypedArray(), + ) + } + } + private companion object { @JvmStatic fun prefixProvider() = listOf( From 6faea4c73b2a291eef1fad14ff002c655c16d090 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Mar 2025 23:07:37 -0400 Subject: [PATCH 341/941] Improve entry checks (#1342) * Add `containsFileEntriesOnly` * Optimize `containsEntries` * Optimize `doesNotContainEntries` * Rename assertions to match the ones in AssertK * Replace `containsNone` checks * Tweak autoRelocation * Cleanups * Revert changes for `preserveLastModifiedCorrectly` --- .../plugins/shadow/ApplicationPluginTest.kt | 4 +- .../gradle/plugins/shadow/BasePluginTest.kt | 14 ++- .../gradle/plugins/shadow/FilteringTest.kt | 44 +++---- .../gradle/plugins/shadow/GroovyPluginTest.kt | 4 +- .../gradle/plugins/shadow/JavaPluginTest.kt | 119 ++++++------------ .../plugins/shadow/KotlinPluginsTest.kt | 8 +- .../gradle/plugins/shadow/MinimizeTest.kt | 29 +++-- .../gradle/plugins/shadow/PublishingTest.kt | 25 ++-- .../gradle/plugins/shadow/RelocationTest.kt | 85 ++++++------- .../gradle/plugins/shadow/ScalaPluginTest.kt | 4 +- .../shadow/caching/FilteringCachingTest.kt | 53 +++----- .../shadow/caching/MinimizationCachingTest.kt | 10 +- .../shadow/caching/RelocationCachingTest.kt | 10 +- .../shadow/caching/ShadowJarCachingTest.kt | 18 +-- .../shadow/caching/TransformerCachingTest.kt | 10 +- .../shadow/transformers/TransformersTest.kt | 6 +- .../gradle/plugins/shadow/util/JarPath.kt | 28 +++-- 17 files changed, 210 insertions(+), 261 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index d7063d5e8..f5369c2ca 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -12,7 +12,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.getContent import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.isWindows @@ -251,7 +251,7 @@ class ApplicationPluginTest : BasePluginTest() { classPathAttr: String? = null, ) { assertThat(jarPath).useAll { - containsEntries(*entriesContained) + containsAtLeast(*entriesContained) getMainAttr(mainClassAttributeKey).isEqualTo(mainClassAttr) getMainAttr(classPathAttributeKey).isEqualTo(classPathAttr) } 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 aaf7adc0b..9b5f6c7f0 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 @@ -386,6 +386,7 @@ abstract class BasePluginTest { } } + @Suppress("ConstPropertyName") companion object { val testKitDir: Path = run { var gradleUserHome = System.getenv("GRADLE_USER_HOME") @@ -398,14 +399,15 @@ abstract class BasePluginTest { val junitJar: Path = requireResourceAsPath("junit-3.8.2.jar") val junitRawEntries: List = JarPath(junitJar) .use { it.entries().toList() } - .filterNot { it.name == "junit3.8.2/" || it.name.startsWith("META-INF/") } + .filterNot { + // This entry is not present in the jar file. + it.name == "junit3.8.2/" + } val junitEntries: Array = junitRawEntries.map { it.name }.toTypedArray() + const val manifestEntry = "META-INF/MANIFEST.MF" - val shadowJar: String = """ - tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name}) - """.trimIndent() - - val runShadow = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)".trim() + val shadowJar: String = "tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name})" + const val runShadow = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)" val commonArguments = listOf( "--warning-mode=fail", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index ca30840cf..d9e00aed0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -1,8 +1,9 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsNone +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeAll @@ -35,7 +36,7 @@ class FilteringTest : BasePluginTest() { fun includeAllDependencies() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries(*entriesInAB) + containsAtLeast(*entriesInAB) } } @@ -52,12 +53,10 @@ class FilteringTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsOnly( "a.properties", "b.properties", - ) - doesNotContainEntries( - "a2.properties", + manifestEntry, ) } } @@ -116,13 +115,10 @@ class FilteringTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsOnly( "d.properties", "my/Passed.class", - ) - doesNotContainEntries( - *entriesInAB, - "c.properties", + manifestEntry, ) } } @@ -142,11 +138,11 @@ class FilteringTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsEntries( + containsAtLeast( "server/Server.class", *junitEntries, ) - doesNotContainEntries( + containsNone( "client/Client.class", ) } @@ -165,12 +161,10 @@ class FilteringTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsEntries( + containsOnly( "client/Client.class", "server/Server.class", - ) - doesNotContainEntries( - *junitEntries, + manifestEntry, ) } } @@ -190,12 +184,10 @@ class FilteringTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsOnly( "a.properties", "b.properties", - ) - doesNotContainEntries( - "a2.properties", + manifestEntry, ) } } @@ -237,12 +229,10 @@ class FilteringTest : BasePluginTest() { private fun commonAssertions() { assertThat(outputShadowJar).useAll { - containsEntries( - *entriesInAB, + containsOnly( "c.properties", - ) - doesNotContainEntries( - "d.properties", + *entriesInAB, + manifestEntry, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt index 132990442..24381a783 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt @@ -2,7 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeEach @@ -35,7 +35,7 @@ class GroovyPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( mainClassEntry, *junitEntries, ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 96dc74f7e..208876661 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -19,8 +19,9 @@ import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttribute import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsNone +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.getContent import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.getStream @@ -85,7 +86,7 @@ class JavaPluginTest : BasePluginTest() { } assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( mainClassEntry, *junitEntries, ) @@ -99,39 +100,6 @@ class JavaPluginTest : BasePluginTest() { } } - @Test - fun includeProjectSources() { - path("src/main/java/my/Passed.java").writeText( - """ - package my; - public class Passed {} - """.trimIndent(), - ) - - projectScriptPath.appendText( - """ - dependencies { - implementation 'junit:junit:3.8.2' - } - $shadowJar { - archiveBaseName = 'fat' - archiveClassifier = '' - archiveVersion = '' - } - """.trimIndent(), - ) - - run(shadowJarTask) - - assertThat(jarPath("build/libs/fat.jar")).useAll { - containsEntries( - "my/Passed.class", - *junitEntries, - ) - doesNotContainEntries("/") - } - } - @Test fun includeProjectDependencies() { writeClientAndServerModules() @@ -139,7 +107,7 @@ class JavaPluginTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsEntries( + containsAtLeast( "client/Client.class", "server/Server.class", *junitEntries, @@ -154,17 +122,13 @@ class JavaPluginTest : BasePluginTest() { run(":server:jar") assertThat(jarPath("server/build/libs/server-1.0.jar")).useAll { - containsEntries( + containsOnly( "server/Server.class", - ) - doesNotContainEntries( - "client/Client.class", - *junitEntries, - "client/junit/framework/Test.class", + manifestEntry, ) } assertThat(jarPath("client/build/libs/client-1.0-all.jar")).useAll { - containsEntries( + containsAtLeast( "client/Client.class", "client/junit/framework/Test.class", ) @@ -180,17 +144,17 @@ class JavaPluginTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsEntries( + containsAtLeast( "client/Client.class", "server/Server.class", *shadowedEntries, ) - doesNotContainEntries( + containsNone( *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), ) } assertThat(jarPath("client/build/libs/client-1.0-all.jar")).useAll { - containsEntries( + containsAtLeast( "client/Client.class", "client/junit/framework/Test.class", ) @@ -258,19 +222,11 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsOnly( "my/Passed.class", "a.properties", "META-INF/a.properties", - ) - doesNotContainEntries( - "META-INF/INDEX.LIST", - "META-INF/a.SF", - "META-INF/a.DSA", - "META-INF/a.RSA", - "META-INF/versions/9/module-info.class", - "META-INF/versions/16/module-info.class", - "module-info.class", + manifestEntry, ) } } @@ -289,8 +245,10 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries(*entriesInA) - doesNotContainEntries(*entriesInB) + containsOnly( + *entriesInA, + manifestEntry, + ) } } @@ -329,7 +287,7 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( "api.properties", "implementation.properties", "runtimeOnly.properties", @@ -352,8 +310,10 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries(*entriesInA) - doesNotContainEntries(*entriesInB) + containsOnly( + *entriesInA, + manifestEntry, + ) } } @@ -361,11 +321,11 @@ class JavaPluginTest : BasePluginTest() { fun defaultCopyingStrategy() { localRepo.module("my", "a", "1.0") { buildJar { - insert("META-INF/MANIFEST.MF", "MANIFEST A") + insert(manifestEntry, "MANIFEST A") } }.module("my", "b", "1.0") { buildJar { - insert("META-INF/MANIFEST.MF", "MANIFEST B") + insert(manifestEntry, "MANIFEST B") } }.publish() @@ -493,8 +453,12 @@ class JavaPluginTest : BasePluginTest() { // Doesn't contain Gradle classes. getMainAttr(classPathAttributeKey).isNull() - containsEntries(*entriesInA) - doesNotContainEntries(*entriesInB) + containsOnly( + "my/plugin/MyPlugin.class", + "META-INF/gradle-plugins/my.plugin.properties", + *entriesInA, + manifestEntry, + ) } } @@ -526,7 +490,7 @@ class JavaPluginTest : BasePluginTest() { run(testShadowJarTask) assertThat(jarPath("build/libs/my-1.0-tests.jar")).useAll { - containsEntries( + containsAtLeast( mainClassEntry, *junitEntries, ) @@ -580,7 +544,7 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(jarPath("build/libs/my-shadow.tar")).useAll { - containsEntries( + containsAtLeast( mainClassEntry, *junitEntries, ) @@ -636,15 +600,15 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsOnly( + "my/", mainClassEntry, - "META-INF/a-1.0.jar", + "Bar/", "Bar/Foo", - ) - doesNotContainEntries( - *entriesInA, - "Foo", - "Foo/", + "META-INF/", + "META-INF/a-1.0.jar", + manifestEntry, + includeDirs = true, ) getContent("Bar/Foo").isEqualTo("Foo") } @@ -655,8 +619,7 @@ class JavaPluginTest : BasePluginTest() { } } assertThat(jarPath(unzipped.name)).useAll { - containsEntries(*entriesInA) - doesNotContainEntries(mainClassEntry) + containsOnly(*entriesInA) } } @@ -689,7 +652,7 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( mainClassEntry, "module-info.class", ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index fefb05451..7ea237aac 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -4,7 +4,7 @@ import assertk.assertThat import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import kotlin.io.path.appendText import kotlin.io.path.writeText @@ -40,7 +40,7 @@ class KotlinPluginsTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( mainClassEntry, *junitEntries, ) @@ -73,7 +73,7 @@ class KotlinPluginsTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( mainClassEntry, *entriesInAB, ) @@ -106,7 +106,7 @@ class KotlinPluginsTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( mainClassEntry, main2ClassEntry, ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index 727651018..62d67a8d5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -2,8 +2,9 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsNone +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test @@ -21,13 +22,13 @@ class MinimizeTest : BasePluginTest() { run(":impl:$SHADOW_JAR_TASK_NAME") assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { - containsEntries( + containsAtLeast( "impl/SimpleEntity.class", "api/Entity.class", "api/UnusedEntity.class", "lib/LibEntity.class", ) - doesNotContainEntries( + containsNone( "junit/framework/Test.class", "lib/UnusedLibEntity.class", ) @@ -55,15 +56,13 @@ class MinimizeTest : BasePluginTest() { run(":impl:$SHADOW_JAR_TASK_NAME") assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { - containsEntries( + containsOnly( "impl/SimpleEntity.class", "api/Entity.class", "api/UnusedEntity.class", "lib/LibEntity.class", "lib/UnusedLibEntity.class", - ) - doesNotContainEntries( - *junitEntries, + manifestEntry, ) } } @@ -93,11 +92,11 @@ class MinimizeTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsEntries( + containsAtLeast( "client/Client.class", "server/Server.class", ) - doesNotContainEntries( + containsNone( "junit/framework/Test.class", ) } @@ -121,11 +120,11 @@ class MinimizeTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsEntries( + containsAtLeast( "server/Server.class", *junitEntries, ) - doesNotContainEntries( + containsNone( "client/Client.class", ) } @@ -148,7 +147,7 @@ class MinimizeTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsEntries( + containsAtLeast( "client/Client.class", "server/Server.class", ) @@ -181,7 +180,7 @@ class MinimizeTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsEntries( + containsAtLeast( "client/Client.class", "server/Server.class", *junitEntries, @@ -197,7 +196,7 @@ class MinimizeTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsEntries( + containsAtLeast( "client/Client.class", "server/Server.class", *junitEntries, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 8711ce0be..8bb9a9cd8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -13,8 +13,9 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsNone +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.gavs import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.squareup.moshi.JsonAdapter @@ -168,7 +169,7 @@ class PublishingTest : BasePluginTest() { publish() assertThat(repoJarPath("my/maven/1.0/maven-1.0-tests.jar")).useAll { - containsEntries(*junitEntries) + containsAtLeast(*junitEntries) } } @@ -274,13 +275,10 @@ class PublishingTest : BasePluginTest() { publish() assertThat(repoJarPath("my/maven-all/1.0/maven-all-1.0.jar")).useAll { - containsEntries( + containsOnly( "aa.properties", "aa2.properties", - ) - doesNotContainEntries( - *entriesInAB, - "bb.properties", + manifestEntry, ) } assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) @@ -365,10 +363,13 @@ class PublishingTest : BasePluginTest() { publish() assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0.jar")).useAll { - doesNotContainEntries(*entriesInAB) + containsNone(*entriesInAB) } assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0-all.jar")).useAll { - containsEntries(*entriesInAB) + containsOnly( + *entriesInAB, + manifestEntry, + ) } assertPomCommon(repoPath("com/acme/maven/1.0/maven-1.0.pom"), arrayOf("my:a:1.0", "my:b:1.0")) @@ -489,8 +490,8 @@ class PublishingTest : BasePluginTest() { private fun assertShadowJarCommon(jarPath: JarPath) { assertThat(jarPath).useAll { - containsEntries(*entriesInA) - doesNotContainEntries(*entriesInB) + containsAtLeast(*entriesInA) + containsNone(*entriesInB) getMainAttr(classPathAttributeKey).isEqualTo("b-1.0.jar") } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 8bff7997c..54305a794 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -10,8 +10,9 @@ import assertk.fail import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.Companion.CONSTANT_TIME_FOR_ZIP_ENTRIES import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsNone +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import java.net.URLClassLoader import kotlin.io.path.appendText import kotlin.io.path.writeText @@ -40,17 +41,28 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) val entryPrefix = relocationPrefix.replace('.', '/') + val shadowedEntries = buildSet { + addAll( + junitEntries.map { "$entryPrefix/$it" } + .filterNot { it.startsWith("$entryPrefix/META-INF/") }, + ) + var parent = entryPrefix + while (parent.isNotEmpty()) { + add("$parent/") + parent = parent.substringBeforeLast('/', "") + } + }.toTypedArray() val result = run(shadowJarTask, "--info") assertThat(outputShadowJar).useAll { - containsEntries( + containsOnly( + "my/", mainClassEntry, - *junitEntries.map { "$entryPrefix/$it" }.toTypedArray(), - ) - doesNotContainEntries( - "$entryPrefix/$mainClassEntry", - *junitEntries, + *shadowedEntries, + "META-INF/", + manifestEntry, + includeDirs = true, ) } // Make sure the relocator count is aligned with the number of unique packages in junit jar. @@ -89,13 +101,13 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( mainClassEntry, *runnerEntries, *frameworkEntries, *otherJunitEntries, ) - doesNotContainEntries( + containsNone( *junitEntries.filter { it !in otherJunitEntries }.toTypedArray(), *otherJunitEntries.map { "a/$it" }.toTypedArray(), *otherJunitEntries.map { "b/$it" }.toTypedArray(), @@ -134,13 +146,13 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( mainClassEntry, *runnerEntries, *frameworkEntries, *otherJunitEntries, ) - doesNotContainEntries( + containsNone( *otherJunitEntries.map { "a/$it" }.toTypedArray(), *otherJunitEntries.map { "b/$it" }.toTypedArray(), ) @@ -181,11 +193,11 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( "my/MyTest.class", *shadowedEntries, ) - doesNotContainEntries( + containsNone( *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), ) } @@ -256,7 +268,7 @@ class RelocationTest : BasePluginTest() { run(":app:$SHADOW_JAR_TASK_NAME") assertThat(jarPath("app/build/libs/app-all.jar")).useAll { - containsEntries( + containsAtLeast( "TEST", "APP-TEST", "test.properties", @@ -264,7 +276,7 @@ class RelocationTest : BasePluginTest() { "app/App.class", *shadowedEntries, ) - doesNotContainEntries( + containsNone( *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), ) } @@ -303,15 +315,11 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsOnly( "bar/Foo.class", "bar/foo.properties", "bar/dep.properties", - ) - doesNotContainEntries( - "foo/Foo.class", - "foo/foo.properties", - "foo/dep.properties", + manifestEntry, ) } } @@ -459,11 +467,9 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsOnly( "kotlin/kotlin.kotlin_builtins", - ) - doesNotContainEntries( - "foo/kotlin/kotlin.kotlin_builtins", + manifestEntry, ) } } @@ -474,7 +480,7 @@ class RelocationTest : BasePluginTest() { val relocateConfig = if (exclude) { """ exclude 'junit/**' - exclude 'META-INF/MANIFEST.MF' + exclude '$manifestEntry' """.trimIndent() } else { "" @@ -496,22 +502,22 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { if (exclude) { - containsEntries( - "META-INF/MANIFEST.MF", + containsAtLeast( *junitEntries, + manifestEntry, ) - doesNotContainEntries( - "foo/META-INF/MANIFEST.MF", + containsNone( + "foo/$manifestEntry", *junitEntries.map { "foo/$it" }.toTypedArray(), ) } else { - containsEntries( - "foo/META-INF/MANIFEST.MF", + containsAtLeast( + "foo/$manifestEntry", *junitEntries.map { "foo/$it" }.toTypedArray(), ) - doesNotContainEntries( - "META-INF/MANIFEST.MF", + containsNone( *junitEntries, + manifestEntry, ) } } @@ -535,14 +541,9 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsOnly( "foo/$mainClassEntry", - "foo/META-INF/MANIFEST.MF", - ) - doesNotContainEntries( - "META-INF/MANIFEST.MF", - *junitEntries, - *junitEntries.map { "foo/$it" }.toTypedArray(), + "foo/$manifestEntry", ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt index 4c3a14a17..ceb200270 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt @@ -2,7 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeEach @@ -35,7 +35,7 @@ class ScalaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries( + containsAtLeast( mainClassEntry, *junitEntries, ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt index 9034c271e..9d6e29806 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt @@ -3,8 +3,8 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.Assert import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText @@ -31,12 +31,10 @@ class FilteringCachingTest : BaseCachingTest() { projectScriptPath.writeText(replaced) assertCompositeExecutions { - containsEntries( - *entriesInAB, + containsOnly( "d.properties", - ) - doesNotContainEntries( - "c.properties", + *entriesInAB, + manifestEntry, ) } } @@ -54,14 +52,12 @@ class FilteringCachingTest : BaseCachingTest() { projectScriptPath.writeText(replaced) assertCompositeExecutions { - containsEntries( + containsOnly( "a2.properties", "b.properties", "c.properties", "d.properties", - ) - doesNotContainEntries( - "a.properties", + manifestEntry, ) } } @@ -83,7 +79,7 @@ class FilteringCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries( + containsAtLeast( *entriesInAB, mainClassEntry, main2ClassEntry, @@ -99,11 +95,11 @@ class FilteringCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries( + containsOnly( mainClassEntry, main2ClassEntry, + manifestEntry, ) - doesNotContainEntries(*entriesInAB) } projectScriptPath.appendText( @@ -115,14 +111,9 @@ class FilteringCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries( + containsOnly( mainClassEntry, - ) - doesNotContainEntries( - main2ClassEntry, - "a.properties", - "a2.properties", - "b.properties", + manifestEntry, ) } @@ -135,11 +126,11 @@ class FilteringCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries( + containsOnly( mainClassEntry, main2ClassEntry, + manifestEntry, ) - doesNotContainEntries(*entriesInAB) } } @@ -155,7 +146,7 @@ class FilteringCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries( + containsAtLeast( mainClassEntry, *junitEntries, ) @@ -172,11 +163,9 @@ class FilteringCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries( + containsOnly( mainClassEntry, - ) - doesNotContainEntries( - *junitEntries, + manifestEntry, ) } } @@ -199,12 +188,10 @@ class FilteringCachingTest : BaseCachingTest() { } private fun Assert.commonAssertions() { - containsEntries( - *entriesInAB, + containsOnly( "c.properties", - ) - doesNotContainEntries( - "d.properties", + *entriesInAB, + manifestEntry, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt index fb72140f1..73f40cf9e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt @@ -1,8 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.caching import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsNone import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test @@ -22,7 +22,7 @@ class MinimizationCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries( + containsAtLeast( "server/Server.class", "client/Client.class", *junitEntries, @@ -40,11 +40,11 @@ class MinimizationCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries( + containsAtLeast( "server/Server.class", *junitEntries, ) - doesNotContainEntries( + containsNone( "client/Client.class", ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index 1d0429484..32b97a282 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.caching -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsNone import kotlin.io.path.appendText import org.junit.jupiter.api.Test @@ -21,7 +21,7 @@ class RelocationCachingTest : BaseCachingTest() { val mainClassEntry = writeClass(withImports = true) assertCompositeExecutions { - containsEntries( + containsAtLeast( mainClassEntry, *junitEntries, ) @@ -38,11 +38,11 @@ class RelocationCachingTest : BaseCachingTest() { .map { it.replace("junit/framework/", "foo/junit/framework/") }.toTypedArray() assertCompositeExecutions { - containsEntries( + containsAtLeast( mainClassEntry, *shadowedEntries, ) - doesNotContainEntries( + containsNone( *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index f107658c1..fafbbb04d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -2,8 +2,8 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries -import com.github.jengelman.gradle.plugins.shadow.util.doesNotContainEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText @@ -25,7 +25,7 @@ class ShadowJarCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries(*entriesInAB) + containsAtLeast(*entriesInAB) } val replaced = projectScriptPath.readText().lines() @@ -34,8 +34,10 @@ class ShadowJarCachingTest : BaseCachingTest() { projectScriptPath.writeText(replaced) assertCompositeExecutions { - containsEntries(*entriesInA) - doesNotContainEntries(*entriesInB) + containsOnly( + *entriesInA, + manifestEntry, + ) } } @@ -50,7 +52,7 @@ class ShadowJarCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsEntries(*entriesInAB) + containsAtLeast(*entriesInAB) } projectScriptPath.appendText( @@ -63,7 +65,7 @@ class ShadowJarCachingTest : BaseCachingTest() { assertExecutionsFromCacheAndUpToDate() assertThat(jarPath("build/libs/foo-1.0-all.jar")).useAll { - containsEntries(*entriesInAB) + containsAtLeast(*entriesInAB) } } @@ -79,7 +81,7 @@ class ShadowJarCachingTest : BaseCachingTest() { ) val assertions = { assertCompositeExecutions { - containsEntries( + containsAtLeast( "c.properties", "d.properties", ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index a2da85a07..100b16e42 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -16,7 +16,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTra import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.deleteExisting @@ -41,7 +41,7 @@ class TransformerCachingTest : BaseCachingTest() { fun shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer() { val assertions = { assertCompositeExecutions { - containsEntries(mainClass) + containsAtLeast(mainClass) } } @@ -68,7 +68,7 @@ class TransformerCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.properties").writeText("foo=bar") val assertions = { name: String -> assertCompositeExecutions { - containsEntries(mainClass, "foo/$name.properties") + containsAtLeast(mainClass, "foo/$name.properties") getContent("foo/$name.properties").isEqualTo("foo=$name") } } @@ -98,7 +98,7 @@ class TransformerCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.xml").writeText("bar") val assertions = { name: String -> assertCompositeExecutions { - containsEntries(mainClass, "foo/$name.xml") + containsAtLeast(mainClass, "foo/$name.xml") getContent("foo/$name.xml").contains("$name") } } @@ -169,7 +169,7 @@ class TransformerCachingTest : BaseCachingTest() { } val assertions = { assertCompositeExecutions { - containsEntries(mainClass) + containsAtLeast(mainClass) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index f54237cc6..f7c366936 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -9,7 +9,7 @@ import assertk.assertions.isNull import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsEntries +import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.getStream import java.util.jar.Attributes as JarAttribute import kotlin.io.path.appendText @@ -121,7 +121,7 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries(*entriesInAB) + containsAtLeast(*entriesInAB) } } @@ -147,7 +147,7 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsEntries(*entriesInAB) + containsAtLeast(*entriesInAB) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index 13302d12d..a55b60e9b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -1,7 +1,9 @@ package com.github.jengelman.gradle.plugins.shadow.util import assertk.Assert -import assertk.fail +import assertk.assertions.containsAtLeast +import assertk.assertions.containsNone +import assertk.assertions.containsOnly import java.io.InputStream import java.nio.file.Path import java.util.jar.JarFile @@ -38,16 +40,18 @@ fun Assert.getContent(entryName: String) = transform { it.getContent(en fun Assert.getMainAttr(name: String) = transform { it.getMainAttr(name) } -fun Assert.containsEntries(vararg entries: String) = given { actual -> - entries.forEach { entry -> - actual.getEntry(entry) - ?: fail("Jar file ${actual.path} does not contain entry $entry in entries: ${actual.entries().toList()}") - } -} +fun Assert.containsAtLeast(vararg entries: String) = toEntries().containsAtLeast(*entries) -fun Assert.doesNotContainEntries(vararg entries: String) = given { actual -> - entries.forEach { entry -> - actual.getEntry(entry) ?: return@forEach - fail("Jar file ${actual.path} contains entry $entry in entries: ${actual.entries().toList()}") - } +fun Assert.containsNone(vararg entries: String) = toEntries().containsNone(*entries) + +fun Assert.containsOnly( + vararg entries: String, + includeDirs: Boolean = false, +) = toEntries().transform { actual -> + // Jar directories are represented as entries ending with a slash. + actual.filter { includeDirs || !it.endsWith('/') } +}.containsOnly(*entries) + +private fun Assert.toEntries() = transform { actual -> + actual.entries().toList().map { it.name } } From 2760e6929d42770189d685bfb164aaef73d6731a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 13 Mar 2025 05:34:44 -0400 Subject: [PATCH 342/941] Document handing DuplicatesStrategy (#1343) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/configuration/merging/README.md | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index b7b72b2dc..aaec7f8bb 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -355,3 +355,45 @@ It must be added using the [`transform`](https://gradleup.com/shadow/api/shadow/ } } ``` + +## Handling Duplicates Strategy + +`ShadowJar` is a subclass of +[`org.gradle.api.tasks.AbstractCopyTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html), +which means it honors the `duplicatesStrategy` property as its parent classes do. There are several strategies to handle: + +- `EXCLUDE`: Do not allow duplicates by ignoring subsequent items to be created at the same path. +- `FAIL`: Throw a `DuplicateFileCopyingException` when subsequent items are to be created at the same path. +- `INCLUDE`: Do not attempt to prevent duplicates. +- `INHERIT`: Uses the same strategy as the parent copy specification. +- `WARN`: Do not attempt to prevent duplicates, but log a warning message when multiple items are to be created at the same path. + +You can see more details about them in +[`DuplicatesStrategy`](https://docs.gradle.org/current/javadoc/org/gradle/api/file/DuplicatesStrategy.html). + +`ShadowJar` recognizes `DuplicatesStrategy.INCLUDE` as the default, if you want to change the strategy, you can +override it like: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or something else. + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or something else. + } + ``` + +Different strategies will lead to different results for `foo/bar` files in the JARs to be merged: + +- `EXCLUDE`: The **first** `foo/bar` file will be included in the final JAR. +- `FAIL`: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicated `foo/bar` files. +- `INCLUDE`: The **last** `foo/bar` file will be included in the final JAR (the default behavior). +- `INHERIT`: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. +- `WARN`: The **last** `foo/bar` file will be included in the final JAR, and a warning message will be logged. From 8a8b0d0446b69497b77c237eafe9613976d5099f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 13 Mar 2025 05:49:34 -0400 Subject: [PATCH 343/941] Fix ResourceTransformer API links (#1344) --- docs/configuration/merging/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index aaec7f8bb..55369ebde 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -1,11 +1,11 @@ # Controlling JAR Content Merging Shadow allows for customizing the process by which the output JAR is generated through the -[`Transformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-transformer/index.html) interface. +[`ResourceTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) interface. This is a concept that has been carried over from the original Maven Shade implementation. -A [`Transformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-transformer/index.html) is invoked for each +A [`ResourceTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) is invoked for each entry in the JAR before being written to the final output JAR. -This allows a [`Transformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-transformer/index.html) to +This allows a [`ResourceTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) to determine if it should process a particular entry and apply any modifications before writing the stream to the output. === "Kotlin" @@ -48,7 +48,7 @@ determine if it should process a particular entry and apply any modifications be } ``` -Additionally, a `Transformer` can accept a `Closure` to configure the provided `Transformer`. +Additionally, a `ResourceTransformer` can accept a `Closure` to configure the provided `ResourceTransformer`. === "Kotlin" @@ -95,7 +95,7 @@ Additionally, a `Transformer` can accept a `Closure` to configure the provided ` } ``` -An instantiated instance of a `Transformer` can also be provided. +An instantiated instance of a `ResourceTransformer` can also be provided. === "Kotlin" @@ -254,7 +254,7 @@ method to add this transformer. ## Merging Log4j2 Plugin Cache Files (`Log4j2Plugins.dat`) -`Log4j2PluginsCacheFileTransformer` is a `Transformer` that merges `META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat` plugin caches from all the jars +`Log4j2PluginsCacheFileTransformer` is a `ResourceTransformer` that merges `META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat` plugin caches from all the jars containing Log4j 2.x Core components. It's a Gradle equivalent of [Log4j Plugin Descriptor Transformer](https://logging.apache.org/log4j/transform/log4j-transform-maven-shade-plugin-extensions.html#log4j-plugin-cache-transformer). === "Kotlin" From 35c151286cd5e9b4b21576cbc7cf704d648dc5b6 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 13 Mar 2025 17:53:01 +0800 Subject: [PATCH 344/941] Run dokkaHtml before MkDocs deploying --- .github/workflows/deploy.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 64ab05649..308dacb9f 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,4 +14,9 @@ jobs: with: # Fetch all tags and branches, so that MkDocs could push incremental updates. fetch-depth: 0 + - uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: true + # Prepare API documentations for MkDocs. + - run: ./gradlew dokkaHtml --no-configuration-cache - uses: ./.github/actions/deploy-site From 36b8cfb9447c6c0cd57f68d8c00e801bee810465 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 14 Mar 2025 11:01:43 +0800 Subject: [PATCH 345/941] Try out JetBrains Mono for code in docs --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 0ac3bca62..35e8c29e9 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -28,7 +28,7 @@ theme: name: Switch to light mode font: text: 'Lato' - code: 'Fira Code' + code: 'JetBrains Mono' features: - content.code.copy - content.code.select From e47a49209744e3848c780ae4b9c75154732537fa Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 14 Mar 2025 11:35:58 +0800 Subject: [PATCH 346/941] Update .gitattributes --- .gitattributes | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 7e3c475f2..954a30846 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ * text=auto eol=lf -gradlew binary -gradlew.bat binary \ No newline at end of file +*.bat text eol=crlf +*.jar binary +*.svg binary From 1adbb42e094c6f8933cf84b021131010e8aa1b40 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 14 Mar 2025 11:37:01 +0800 Subject: [PATCH 347/941] Optimize SVG files by SVGO --- docs/images/logo+type.orig.svg | 111 +- docs/images/logo+type.svg | 6100 +------------------------------- docs/images/logo.orig.svg | 65 +- docs/images/logo.svg | 3836 +------------------- 4 files changed, 4 insertions(+), 10108 deletions(-) diff --git a/docs/images/logo+type.orig.svg b/docs/images/logo+type.orig.svg index 730bdff84..53a67c830 100644 --- a/docs/images/logo+type.orig.svg +++ b/docs/images/logo+type.orig.svg @@ -1,110 +1 @@ - - - - - - + \ No newline at end of file diff --git a/docs/images/logo+type.svg b/docs/images/logo+type.svg index 1fa96b75d..2cb5861ec 100644 --- a/docs/images/logo+type.svg +++ b/docs/images/logo+type.svg @@ -1,6099 +1 @@ - - - - + \ No newline at end of file diff --git a/docs/images/logo.orig.svg b/docs/images/logo.orig.svg index 20f1be3e9..b5ed5c6d2 100644 --- a/docs/images/logo.orig.svg +++ b/docs/images/logo.orig.svg @@ -1,64 +1 @@ - - - - - - + \ No newline at end of file diff --git a/docs/images/logo.svg b/docs/images/logo.svg index 4dbc6611b..016822435 100644 --- a/docs/images/logo.svg +++ b/docs/images/logo.svg @@ -1,3835 +1 @@ - - - - + \ No newline at end of file From a9433770c702903525a9a9050be6cae7e0c83b01 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 07:49:11 +0800 Subject: [PATCH 348/941] Update kotlin monorepo to v2.1.20-RC3 (#1347) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2f428546c..5da6cc176 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.1.20-RC2" +kotlin = "2.1.20-RC3" moshi = "1.15.2" [libraries] From d872d45e24522c31fb43f205417ff8c3ce90f182 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 15 Mar 2025 07:49:21 +0800 Subject: [PATCH 349/941] Update dependency org.junit:junit-bom to v5.12.1 (#1346) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5da6cc176..ce2b2ad53 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,7 +27,7 @@ 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. ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" -junit-bom = "org.junit:junit-bom:5.12.0" +junit-bom = "org.junit:junit-bom:5.12.1" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From 8464ed09d5e2c2f141a05f367d27444cdb8b1929 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 14 Mar 2025 20:30:48 -0400 Subject: [PATCH 350/941] Note the mixed usages of ResourceTransformer and DuplicatesStrategy (#1349) --- docs/configuration/merging/README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 55369ebde..6d231b6ed 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -397,3 +397,30 @@ Different strategies will lead to different results for `foo/bar` files in the J - `INCLUDE`: The **last** `foo/bar` file will be included in the final JAR (the default behavior). - `INHERIT`: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. - `WARN`: The **last** `foo/bar` file will be included in the final JAR, and a warning message will be logged. + +**NOTE:** The `duplicatesStrategy` takes precedence over transforming and relocating. If you mix the usages of +`duplicatesStrategy` and `ResourceTransformer` like below: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + mergeServiceFiles() + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + mergeServiceFiles() + } + ``` + +The `ServiceFileTransformer` will not work as expected because the `duplicatesStrategy` will exclude the duplicated +service files beforehand. However, this behavior might be what you expected for duplicated `foo/bar` files, preventing +them from being included. +Want `ResourceTransformer`s and `duplicatesStrategy` to work together? There is a way to achieve this, leave the +`duplicatesStrategy` as `INCLUDE` and declare a custom `ResourceTransformer` to handle the duplicated files. From 0c15f49f44b29bfe663ba317a37a717a0c1a9c05 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 14 Mar 2025 20:42:58 -0400 Subject: [PATCH 351/941] Deploy sites by official actions (#1350) * Move dokka step into .github/actions/deploy-site/action.yml * Use `actions/upload-pages-artifact` and `actions/deploy-pages` Refs https://github.com/squidfunk/mkdocs-material/blob/0e75aef9ea9028dc5a2dea34fb5da8afb14911ae/.github/workflows/documentation.yml#L92-L112 --- .github/actions/deploy-site/action.yml | 17 ++++++++++++----- .github/workflows/deploy.yml | 5 ----- .github/workflows/release.yml | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.github/actions/deploy-site/action.yml b/.github/actions/deploy-site/action.yml index 8ef83d06d..238768994 100644 --- a/.github/actions/deploy-site/action.yml +++ b/.github/actions/deploy-site/action.yml @@ -4,12 +4,19 @@ description: 'Deploy site to GitHub Pages' runs: using: 'composite' steps: - - name: Deploy Site + - uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: true + - name: Prepare API documentation + shell: bash + run: ./gradlew dokkaHtml --no-configuration-cache + - name: Build Site shell: bash run: | - git config --global user.name 'github-actions[bot]' - git config --global user.email 'github-actions[bot]@users.noreply.github.com' # Don't cache it to track updates. pip install mkdocs-material - # Incremental pushes, make sure gh-pages branch is fetched by checkout. - mkdocs gh-deploy + mkdocs build + - uses: actions/upload-pages-artifact@v3 + with: + path: site + - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 308dacb9f..64ab05649 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,9 +14,4 @@ jobs: with: # Fetch all tags and branches, so that MkDocs could push incremental updates. fetch-depth: 0 - - uses: gradle/actions/setup-gradle@v4 - with: - cache-read-only: true - # Prepare API documentations for MkDocs. - - run: ./gradlew dokkaHtml --no-configuration-cache - uses: ./.github/actions/deploy-site diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1e236cb4e..000df4bca 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,7 +24,7 @@ jobs: with: cache-read-only: true # Disable CC due to https://github.com/gradle/gradle/issues/22779 - - run: ./gradlew publish publishPlugins dokkaHtml --no-configuration-cache + - run: ./gradlew publish publishPlugins --no-configuration-cache env: GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_KEY }} GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_SECRET }} From 0f5714376079fe487d633c8dbb9cfd2727681a1d Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 15 Mar 2025 08:47:47 +0800 Subject: [PATCH 352/941] Add missing things --- .github/workflows/deploy.yml | 4 ++++ .github/workflows/release.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 64ab05649..59791732e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,8 +7,12 @@ jobs: deploy: runs-on: ubuntu-latest if: github.repository == 'GradleUp/shadow' + environment: + name: github-pages permissions: contents: write + id-token: write + pages: write steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 000df4bca..5d8880988 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,8 +9,12 @@ jobs: release: runs-on: ubuntu-latest if: github.repository == 'GradleUp/shadow' + environment: + name: github-pages permissions: contents: write + id-token: write + pages: write steps: - uses: actions/checkout@v4 with: From 636fe6f78d99b13d6d831dabd76c654ba300b643 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 15 Mar 2025 08:56:25 +0800 Subject: [PATCH 353/941] Replace fork checks --- .github/workflows/ci.yml | 2 +- .github/workflows/deploy.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c393c30b..e6ae09de4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: publish-snapshot: needs: build runs-on: ubuntu-latest - if: github.repository == 'GradleUp/shadow' && github.ref == 'refs/heads/main' + if: github.event.repository.fork == false && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 59791732e..9eb54494c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,7 +6,7 @@ on: jobs: deploy: runs-on: ubuntu-latest - if: github.repository == 'GradleUp/shadow' + if: github.event.repository.fork == false environment: name: github-pages permissions: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5d8880988..d73223b52 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ on: jobs: release: runs-on: ubuntu-latest - if: github.repository == 'GradleUp/shadow' + if: github.event.repository.fork == false environment: name: github-pages permissions: From 2e562a453916c63bce2bd8569835275460d21eae Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 15 Mar 2025 09:02:11 +0800 Subject: [PATCH 354/941] No need to fetch all history --- .github/workflows/deploy.yml | 3 --- .github/workflows/release.yml | 3 --- 2 files changed, 6 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 9eb54494c..1be19d144 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,7 +15,4 @@ jobs: pages: write steps: - uses: actions/checkout@v4 - with: - # Fetch all tags and branches, so that MkDocs could push incremental updates. - fetch-depth: 0 - uses: ./.github/actions/deploy-site diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d73223b52..01ecb72ee 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,9 +17,6 @@ jobs: pages: write steps: - uses: actions/checkout@v4 - with: - # Fetch all tags and branches, so that MkDocs could push incremental updates. - fetch-depth: 0 - uses: actions/setup-java@v4 with: distribution: 'zulu' From d76cfda71ec05a959c8ca8992cc2364f172e9a02 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 17 Mar 2025 14:37:22 +0800 Subject: [PATCH 355/941] Optimize .idea/icon.svg --- .idea/icon.svg | 3836 +----------------------------------------------- 1 file changed, 1 insertion(+), 3835 deletions(-) diff --git a/.idea/icon.svg b/.idea/icon.svg index 4dbc6611b..016822435 100644 --- a/.idea/icon.svg +++ b/.idea/icon.svg @@ -1,3835 +1 @@ - - - - + \ No newline at end of file From 18bb4eaf78f5f37cf73f27a74e40235b40add915 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Mar 2025 14:50:07 +0800 Subject: [PATCH 356/941] Delete NOTICE --- NOTICE | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 NOTICE diff --git a/NOTICE b/NOTICE deleted file mode 100644 index ae19c72f5..000000000 --- a/NOTICE +++ /dev/null @@ -1,5 +0,0 @@ -Shadow Gradle Plugin -Copyright (c) 2013-2025 John Engelman and Shadow contributors. All Rights Reserved. - -This product is licensed to you under the Apache License, Version 2.0 (the "License"). -You may not use this product except in compliance with the License. From 377a9bcc9986c58d98ac4d65b1984651eebb1785 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Mar 2025 14:52:39 +0800 Subject: [PATCH 357/941] Create CONTRIBUTING.md --- .github/CONTRIBUTING.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .github/CONTRIBUTING.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md new file mode 100644 index 000000000..7abbcca76 --- /dev/null +++ b/.github/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contributing to Shadow Gradle Plugin + +Thank you for considering contributing From 188972040e4700107b8f31325b160efba69de4f7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Mar 2025 21:38:56 -0400 Subject: [PATCH 358/941] See Also: Gradle APIs in KDoc (#1352) --- .../jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt | 2 ++ .../jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 31aaa0164..3cddbbd7c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -23,6 +23,8 @@ import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator * A [Plugin] which packages and runs a project as a Java Application using the shadowed jar. * * Modified from [org.gradle.api.plugins.ApplicationPlugin.java](https://github.com/gradle/gradle/blob/45a20d82b623786d19b50185e595adf3d7b196b2/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java). + * + * @see ApplicationPlugin */ public abstract class ShadowApplicationPlugin : Plugin { override fun apply(project: Project) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 9688a2c71..9eb262ffd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -28,6 +28,8 @@ import org.objectweb.asm.commons.ClassRemapper /** * Modified from [org.gradle.api.internal.file.archive.ZipCopyAction.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java). + * + * @see [org.gradle.api.internal.file.archive.ZipCopyAction] */ public open class ShadowCopyAction( private val zipFile: File, From 6f93d9d5f172bc1cae36c8101ad3dada9befa3d3 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 18 Mar 2025 09:43:51 +0800 Subject: [PATCH 359/941] Unify @see styles --- .../jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt | 2 +- .../jengelman/gradle/plugins/shadow/relocation/Relocator.kt | 2 +- .../gradle/plugins/shadow/transformers/ResourceTransformer.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 3cddbbd7c..198e13cb2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -24,7 +24,7 @@ import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator * * Modified from [org.gradle.api.plugins.ApplicationPlugin.java](https://github.com/gradle/gradle/blob/45a20d82b623786d19b50185e595adf3d7b196b2/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java). * - * @see ApplicationPlugin + * @see [ApplicationPlugin] */ public abstract class ShadowApplicationPlugin : Plugin { override fun apply(project: Project) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt index d6b48d295..073c29726 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -29,7 +29,7 @@ public interface Relocator { * In other words, it has its appropriate inputs annotated so that Gradle can consider them when * determining the cache key. * - * @see CacheableTransformer + * @see [CacheableTransformer] */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt index 134e4db9f..d35c4acbc 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt @@ -64,7 +64,7 @@ public interface ResourceTransformer { * In other words, it has its appropriate inputs annotated so that Gradle can consider them when * determining the cache key. * - * @see CacheableRelocator + * @see [CacheableRelocator] */ @Retention(AnnotationRetention.RUNTIME) @Target(AnnotationTarget.CLASS) From bdd127588a0f8585551222559697dd2fe820b7a3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Mar 2025 22:32:23 -0400 Subject: [PATCH 360/941] No need to make runShadow depend on installShadowDist (#1353) - Split `installShadowOutputs` from `integrationWithApplicationPluginAndJavaToolchains`. - Tweak `canOverrideMainClassAttrInManifestBlock`. --- docs/changes/README.md | 1 + .../plugins/shadow/ApplicationPluginTest.kt | 21 ++++++++++++------- .../gradle/plugins/shadow/BasePluginTest.kt | 2 ++ .../plugins/shadow/ShadowApplicationPlugin.kt | 5 +---- 4 files changed, 18 insertions(+), 11 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 702487abe..18c70f788 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -16,6 +16,7 @@ - `ShadowSpec` no longer extends `CopySpec`. - Overload `relocate`, `transform` and things for better usability in Kotlin. - **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) +- `runShadow` no longer depends on `installShadowDist`. ([#1353](https://github.com/GradleUp/shadow/pull/1353)) **Fixed** diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index f5369c2ca..9082e7763 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -6,7 +6,6 @@ import assertk.assertions.contains import assertk.assertions.containsOnly import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo -import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.DISTRIBUTION_NAME import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey @@ -43,9 +42,6 @@ class ApplicationPluginTest : BasePluginTest() { toolchain.languageVersion = JavaLanguageVersion.of(17) } """.trimIndent(), - applicationBlock = """ - applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED'] - """.trimIndent(), settingsBlock = """ plugins { id 'org.gradle.toolchains.foojay-resolver-convention' @@ -65,6 +61,17 @@ class ApplicationPluginTest : BasePluginTest() { "Hello, World! (foo) from Main", "Refs: junit.framework.Test", ) + } + + @Test + fun installShadowOutputs() { + prepare( + mainClassWithImports = true, + dependenciesBlock = "implementation 'junit:junit:3.8.2'", + applicationBlock = "applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED']", + ) + + run(installShadowDistTask) val installPath = path("build/install/") assertThat(installPath.walkEntries()).containsOnly( @@ -106,7 +113,7 @@ class ApplicationPluginTest : BasePluginTest() { fun installShadowDoesNotExecuteDependentShadowTask() { prepare() - run(SHADOW_INSTALL_TASK_NAME) + run(installShadowDistTask) commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) } @@ -134,9 +141,9 @@ class ApplicationPluginTest : BasePluginTest() { } } - assertions(run(runShadowTask).output, "foo") + assertions(run(runShadowTask, shadowJarTask).output, "foo") commonAssertions( - jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar"), + jarPath("build/libs/myapp-1.0-all.jar"), entriesContained = entriesInA + arrayOf(mainClass, main2ClassEntry), mainClassAttr = "my.Main2", ) 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 9b5f6c7f0..10779b2a1 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 @@ -6,6 +6,7 @@ import assertk.assertThat import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import assertk.assertions.isNotNull +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath @@ -56,6 +57,7 @@ abstract class BasePluginTest { val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" val runShadowTask = ":$SHADOW_RUN_TASK_NAME" + val installShadowDistTask = ":$SHADOW_INSTALL_TASK_NAME" val shadowDistZipTask = ":shadowDistZip" val projectScriptPath: Path get() = path("build.gradle") diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 198e13cb2..fc0ba4c2f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -40,10 +40,7 @@ public abstract class ShadowApplicationPlugin : Plugin { task.description = "Runs this project as a JVM application using the shadow jar" task.group = ApplicationPlugin.APPLICATION_GROUP - val jarFile = tasks.installShadowDist.zip(tasks.shadowJar) { i, s -> - i.destinationDir.resolve("lib/${s.archiveFile.get().asFile.name}") - } - task.classpath(jarFile) + task.classpath = files(tasks.shadowJar) with(applicationExtension) { task.mainModule.set(mainModule) From 84d1712b94ea7134a0963216886e7184b39b43b9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 18 Mar 2025 03:45:06 -0400 Subject: [PATCH 361/941] Use relative API links in docs (#1354) It works for both local and remote sites. --- docs/configuration/README.md | 4 +-- docs/configuration/dependencies/README.md | 10 +++---- docs/configuration/merging/README.md | 34 +++++++++++------------ docs/custom-tasks/README.md | 2 +- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 5ecba474b..daa4c82da 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -4,10 +4,10 @@ api: api/com/github/jengelman/gradle/plugins/shadow # Configuring Shadow -The [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type extends from Gradle's +The [`ShadowJar`](../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type extends from Gradle's [`Jar`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) type. This means that all attributes and methods available on `Jar` are also available on -[`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html). +[`ShadowJar`](../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html). Refer the _Gradle User Guide_ for [Jar](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) for details. diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 488931ca5..834ffe3a7 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -3,7 +3,7 @@ Shadow configures the default `shadowJar` task to merge all dependencies from the project's `runtimeClasspath` configuration into the final JAR. The configurations from which to source dependencies for the merging can be configured using the `configurations` property -of the [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type. +of the [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type. === "Kotlin" @@ -25,9 +25,9 @@ The above code sample would configure the `shadowJar` task to merge dependencies This means any dependency declared in the `runtimeOnly` configuration would be **not** be included in the final JAR. > Note the literal use of `project.configurations` when setting the `configurations` attribute of a -[`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. +[`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. This is **required**. It may be tempting to specify `configurations = [configurations.compile]` but this will not -have the intended effect, as `configurations.compile` will try to delegate to the `configurations` property of the [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task instead of the `project` +have the intended effect, as `configurations.compile` will try to delegate to the `configurations` property of the [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task instead of the `project` ## Embedding Jar Files Inside Your Shadow Jar @@ -72,7 +72,7 @@ See also [Adding Extra Files](../README.md#adding-extra-files) ## Filtering Dependencies Individual dependencies can be filtered from the final JAR by using the `dependencies` block of a -[`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. +[`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. Dependency filtering does **not** apply to transitive dependencies. That is, excluding a dependency does not exclude any of its dependencies from the final JAR. @@ -306,7 +306,7 @@ You can also use type-safe project accessors or version catalog accessors to fil ### Programmatically Selecting Dependencies to Filter If more complex decisions are needed to select the dependencies to be included, the -[`dependencies`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html) +[`dependencies`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html) block provides a method that accepts a `Closure` for selecting dependencies. === "Kotlin" diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 6d231b6ed..d1d2967de 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -1,11 +1,11 @@ # Controlling JAR Content Merging Shadow allows for customizing the process by which the output JAR is generated through the -[`ResourceTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) interface. +[`ResourceTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) interface. This is a concept that has been carried over from the original Maven Shade implementation. -A [`ResourceTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) is invoked for each +A [`ResourceTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) is invoked for each entry in the JAR before being written to the final output JAR. -This allows a [`ResourceTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) to +This allows a [`ResourceTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) to determine if it should process a particular entry and apply any modifications before writing the stream to the output. === "Kotlin" @@ -147,7 +147,7 @@ At runtime, this file is read and used to configure library or application behav Multiple dependencies may use the same service descriptor file name. In this case, it is generally desired to merge the content of each instance of the file into a single output file. -The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) +The [`ServiceFileTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) class is used to perform this merging. By default, it will merge each copy of a file under `META-INF/services` into a single file in the output JAR. @@ -168,17 +168,17 @@ single file in the output JAR. ``` The above code snippet is a convenience syntax for calling -[`transform(ServiceFileTransformer.class)`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html). +[`transform(ServiceFileTransformer.class)`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html). > Groovy Extension Module descriptor files (located at `META-INF/services/org.codehaus.groovy.runtime.ExtensionModule`) -are ignored by the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html). +are ignored by the [`ServiceFileTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html). This is due to these files having a different syntax than standard service descriptor files. -Use the [`mergeGroovyExtensionModules()`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/merge-groovy-extension-modules.html) method to merge +Use the [`mergeGroovyExtensionModules()`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/merge-groovy-extension-modules.html) method to merge these files if your dependencies contain them. ### Configuring the Location of Service Descriptor Files -By default, the [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) +By default, the [`ServiceFileTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) is configured to merge files in `META-INF/services`. This directory can be overridden to merge descriptor files in a different location. @@ -204,7 +204,7 @@ This directory can be overridden to merge descriptor files in a different locati #### Excluding/Including Specific Service Descriptor Files From Merging -The [`ServiceFileTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) +The [`ServiceFileTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) class supports specifying specific files to include or exclude from merging. === "Kotlin" @@ -231,9 +231,9 @@ class supports specifying specific files to include or exclude from merging. Shadow provides a specific transformer for dealing with Groovy extension module files. This is due to their special syntax and how they need to be merged together. -The [`GroovyExtensionModuleTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-groovy-extension-module-transformer/index.html) +The [`GroovyExtensionModuleTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-groovy-extension-module-transformer/index.html) will handle these files. -The [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task also provides a short syntax +The [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task also provides a short syntax method to add this transformer. === "Kotlin" @@ -276,11 +276,11 @@ containing Log4j 2.x Core components. It's a Gradle equivalent of [Log4j Plugin ## Appending Text Files Generic text files can be appended together using the -[`AppendingTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html). +[`AppendingTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html). Each file is appended using separators (defaults to `\n`) to separate content. -The [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task provides a short syntax +The [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task provides a short syntax method of -[`append(String)`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html) to +[`append(String)`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html) to configure this transformer. === "Kotlin" @@ -331,10 +331,10 @@ configure this transformer. ## Appending XML Files XML files require a special transformer for merging. -The [`XmlAppendingTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html) +The [`XmlAppendingTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html) reads each XML document and merges each root element into a single document. -There is no short syntax method for the [`XmlAppendingTransformer`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html). -It must be added using the [`transform`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html)) methods. +There is no short syntax method for the [`XmlAppendingTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html). +It must be added using the [`transform`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html)) methods. === "Kotlin" diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index 253fae1e3..218c0577c 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -1,7 +1,7 @@ # Creating a Custom ShadowJar Task The built in `shadowJar` task only provides an output for the `main` source set of the project. -It is possible to add arbitrary [`ShadowJar`](https://gradleup.com/shadow/api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) +It is possible to add arbitrary [`ShadowJar`](../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) tasks to a project. When doing so, ensure that the `configurations` property is specified to inform Shadow which dependencies to merge into the output. From 546a92583fa68656ebca47420ef0e06a2bef956c Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 18 Mar 2025 15:52:52 +0800 Subject: [PATCH 362/941] Prepare version 9.0.0-beta11 --- docs/changes/README.md | 5 +++-- gradle.properties | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 18c70f788..ab8dd3832 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased] +## [v9.0.0-beta11] (2025-03-18) **Added** @@ -609,7 +609,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Tue, 18 Mar 2025 15:53:56 +0800 Subject: [PATCH 363/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ab8dd3832..09b9c0d05 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased] + + ## [v9.0.0-beta11] (2025-03-18) **Added** diff --git a/gradle.properties b/gradle.properties index c847498f5..962bd3557 100644 --- a/gradle.properties +++ b/gradle.properties @@ -11,7 +11,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta11 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 2fcbfd1f27697479a2438d502ab94c2fe622fba0 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 18 Mar 2025 17:58:23 +0800 Subject: [PATCH 364/941] Remove redundant '+' for min Gradle column --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index edc69efe2..ea997ca1b 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,6 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. | 5.2.0 - 6.1.0 | 5.x - 6.x | 7 | | 6.1.0+ | 6.x | 8 | | 7.0.0+ | 7.x | 8 | -| 8.0.0+ | 8.0+ | 8 | -| 8.3.0+ | 8.3+ | 8 | -| 9.0.0+ | 8.3+ | 11 | +| 8.0.0+ | 8.0 | 8 | +| 8.3.0+ | 8.3 | 8 | +| 9.0.0+ | 8.3 | 11 | From b12aba90e3ccdd40af3187090e6d90ff7c3e8bf4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 19 Mar 2025 00:53:43 -0400 Subject: [PATCH 365/941] Move the group of ShadowJar from shadow to build (#1355) Aligns with `Jar`, `SourceJarTask`, and so on. --- api/shadow.api | 1 - docs/changes/README.md | 4 ++++ docs/custom-tasks/README.md | 4 ++-- docs/publishing/README.md | 4 ++-- .../github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 2 +- .../github/jengelman/gradle/plugins/shadow/PublishingTest.kt | 2 +- .../jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt | 1 - .../jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt | 3 ++- 8 files changed, 12 insertions(+), 9 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index eb5e4c6ce..741150c7b 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -24,7 +24,6 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugi public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$Companion; public static final field DISTRIBUTION_NAME Ljava/lang/String; public static final field EXTENSION_NAME Ljava/lang/String; - public static final field GROUP_NAME Ljava/lang/String; public static final field SHADOW Ljava/lang/String; public fun ()V public synthetic fun apply (Ljava/lang/Object;)V diff --git a/docs/changes/README.md b/docs/changes/README.md index 09b9c0d05..3ae9434b2 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased] +**Changed** + +- Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) + ## [v9.0.0-beta11] (2025-03-18) diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index 218c0577c..261985cb8 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -9,7 +9,7 @@ dependencies to merge into the output. ```kotlin val testShadowJar by tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { - group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + group = LifecycleBasePlugin.BUILD_GROUP description = "Create a combined JAR of project and test dependencies" archiveClassifier = "tests" @@ -33,7 +33,7 @@ dependencies to merge into the output. ```groovy def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + group = LifecycleBasePlugin.BUILD_GROUP description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 6bf2126db..da6146551 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -163,7 +163,7 @@ It is possible to publish a custom `ShadowJar` task's output via the [`MavenPubl } val testShadowJar by tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { - group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + group = LifecycleBasePlugin.BUILD_GROUP description = "Create a combined JAR of project and test dependencies" archiveClassifier = "tests" from(sourceSets.test.map { it.output }) @@ -196,7 +196,7 @@ It is possible to publish a custom `ShadowJar` task's output via the [`MavenPubl } def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + group = LifecycleBasePlugin.BUILD_GROUP description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 208876661..aa9014eec 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -475,7 +475,7 @@ class JavaPluginTest : BasePluginTest() { testImplementation 'junit:junit:3.8.2' } def $testShadowJarTask = tasks.register('$testShadowJarTask', ${ShadowJar::class.java.name}) { - group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + group = LifecycleBasePlugin.BUILD_GROUP description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 8bb9a9cd8..a87c23710 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -148,7 +148,7 @@ class PublishingTest : BasePluginTest() { publishConfiguration( projectBlock = """ def testShadowJar = tasks.register('testShadowJar', ${ShadowJar::class.java.name}) { - group = com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.GROUP_NAME + group = LifecycleBasePlugin.BUILD_GROUP description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 241b491d0..1539d6153 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -21,7 +21,6 @@ public abstract class ShadowBasePlugin : Plugin { public companion object { public const val SHADOW: String = "shadow" - public const val GROUP_NAME: String = SHADOW public const val EXTENSION_NAME: String = SHADOW public const val CONFIGURATION_NAME: String = SHADOW public const val COMPONENT_NAME: String = SHADOW 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 b93e64ee7..08a6351cc 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.component.SoftwareComponentFactory import org.gradle.api.plugins.JavaPlugin import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider +import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin public abstract class ShadowJavaPlugin @Inject constructor( @@ -127,7 +128,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( action: Action, ): TaskProvider { return tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> - task.group = ShadowBasePlugin.GROUP_NAME + task.group = LifecycleBasePlugin.BUILD_GROUP task.description = "Create a combined JAR of project and runtime dependencies" task.archiveClassifier.set("all") task.exclude( From 8ce361d3bc2fc7b45aa7e55313f6854252650ffe Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 20 Mar 2025 10:25:22 +0800 Subject: [PATCH 366/941] H3 for configuring the location of service descriptor files --- docs/configuration/merging/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index d1d2967de..5f5360f82 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -202,7 +202,7 @@ This directory can be overridden to merge descriptor files in a different locati } ``` -#### Excluding/Including Specific Service Descriptor Files From Merging +### Excluding/Including Specific Service Descriptor Files From Merging The [`ServiceFileTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) class supports specifying specific files to include or exclude from merging. From b7d66781bde738f1ca1f4c02ab82b71833e70a10 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 12:54:58 +0000 Subject: [PATCH 367/941] Update kotlin monorepo to v2.1.20 (#1358) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ce2b2ad53..26adf606a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.1.20-RC3" +kotlin = "2.1.20" moshi = "1.15.2" [libraries] From b3305264448d077367a7c8a3c1afe792d4fafa07 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 23 Mar 2025 14:44:56 +0800 Subject: [PATCH 368/941] Update TODO --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 6d012d90f..53e2b4cfd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -22,7 +22,7 @@ develocity { buildScan { termsOfUseUrl = "https://gradle.com/terms-of-service" termsOfUseAgree = "yes" - // TODO: workaround for https://github.com/gradle/gradle/issues/22879. + // TODO: https://github.com/gradle/gradle/issues/22879 val isCI = providers.environmentVariable("CI").isPresent publishing.onlyIf { isCI } } From 3ad0c1538394733cabf46268d47b648cd1d359d2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 23 Mar 2025 02:48:32 -0400 Subject: [PATCH 369/941] Apply Gradle's strict plugin types validation (#1359) --- build.gradle.kts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 508d41574..d78661f01 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -147,6 +147,11 @@ tasks.pluginUnderTestMetadata { ) } +tasks.validatePlugins { + // TODO: https://github.com/gradle/gradle/issues/22879 + enableStricterValidation = true +} + tasks.check { dependsOn(tasks.withType()) } From c376692d05a76c74cf08c13e14cb23249f9ca8d9 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 23 Mar 2025 14:49:21 +0800 Subject: [PATCH 370/941] Fix issue link --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index d78661f01..fd34eea09 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -148,7 +148,7 @@ tasks.pluginUnderTestMetadata { } tasks.validatePlugins { - // TODO: https://github.com/gradle/gradle/issues/22879 + // TODO: https://github.com/gradle/gradle/issues/22600 enableStricterValidation = true } From b525696a86f02b9f8aa83a2fc98fcae8eb1adf0f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 23 Mar 2025 23:40:10 -0400 Subject: [PATCH 371/941] Tweak Path to String (#1360) * Remove `kotlin.io.path.pathString` * Override `toString` for `JarPath` --- .../gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt | 3 +-- .../com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt index 5047a05fd..b63095985 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt @@ -5,7 +5,6 @@ import java.util.regex.Pattern import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.Path import kotlin.io.path.name -import kotlin.io.path.pathString import kotlin.io.path.readText import kotlin.io.path.relativeTo import kotlin.io.path.walk @@ -28,7 +27,7 @@ object CodeSnippetExtractor { lang: DslLang, markdownPath: Path, ): List { - val relativeDocPath = markdownPath.relativeTo(docRoot).pathString + val relativeDocPath = markdownPath.relativeTo(docRoot).toString() return createSnippets(markdownPath.readText(), lang).map { (lineNumber, snippet) -> SnippetExecutable.create( lang, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index a55b60e9b..6e81379a4 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -25,6 +25,8 @@ class JarPath(val path: Path) : fun getMainAttr(name: String): String? { return manifest.mainAttributes.getValue(name) } + + override fun toString(): String = path.toString() } fun ZipFile.getContent(entryName: String): String { From 7279e90dcc7401d306151e2f2c46c8af42894883 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 24 Mar 2025 04:34:07 -0400 Subject: [PATCH 372/941] Update configurations assignments and fix typos (#1361) --- docs/configuration/dependencies/README.md | 6 +++--- docs/custom-tasks/README.md | 4 ++-- docs/publishing/README.md | 6 +++--- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 2 +- .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 834ffe3a7..eab1e43b9 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -9,7 +9,7 @@ of the [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow ```kotlin tasks.shadowJar { - configurations = provider { listOf(project.configurations.runtimeClasspath.get()) } + configurations = project.configurations.compileClasspath.map { listOf(it) } } ``` @@ -17,7 +17,7 @@ of the [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - configurations = [project.configurations.compileClasspath] + configurations = project.configurations.named('compileClasspath').map { [it] } } ``` @@ -26,7 +26,7 @@ This means any dependency declared in the `runtimeOnly` configuration would be * > Note the literal use of `project.configurations` when setting the `configurations` attribute of a [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. -This is **required**. It may be tempting to specify `configurations = [configurations.compile]` but this will not +This is **required**. It may be tempting to specify `configurations = [configurations.compileClasspath]` but this will not have the intended effect, as `configurations.compile` will try to delegate to the `configurations` property of the [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task instead of the `project` ## Embedding Jar Files Inside Your Shadow Jar diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index 261985cb8..11e317959 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -14,7 +14,7 @@ dependencies to merge into the output. archiveClassifier = "tests" from(sourceSets.test.map { it.output }) - configurations = provider { listOf(project.configurations["testRuntimeClasspath"]) } + configurations = project.configurations.testRuntimeClasspath.map { listOf(it) } manifest { // Optionally, set the main class for the JAR. @@ -38,7 +38,7 @@ dependencies to merge into the output. archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } - configurations = provider { [project.configurations.testRuntimeClasspath] } + configurations = project.configurations.named('testRuntimeClasspath').map { [it] } manifest { // Optionally, set the main class for the JAR. diff --git a/docs/publishing/README.md b/docs/publishing/README.md index da6146551..e888b53f4 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -60,7 +60,7 @@ published artifact. No other dependencies are automatically configured for inclusion in the POM file. For example, excluded dependencies are **not** automatically added to the POM file or if the configuration for merging are modified by specifying -`shadowJar.configurations = [configurations.myconfiguration]`, there is no automatic +`shadowJar.configurations = [configurations.myConfiguration]`, there is no automatic configuration of the POM file. This automatic configuration occurs _only_ when using the above methods for @@ -167,7 +167,7 @@ It is possible to publish a custom `ShadowJar` task's output via the [`MavenPubl description = "Create a combined JAR of project and test dependencies" archiveClassifier = "tests" from(sourceSets.test.map { it.output }) - configurations = provider { listOf(project.configurations["testRuntimeClasspath"]) } + configurations = project.configurations.testRuntimeClasspath.map { listOf(it) } } dependencies { @@ -200,7 +200,7 @@ It is possible to publish a custom `ShadowJar` task's output via the [`MavenPubl description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } - configurations = provider { [project.configurations.testRuntimeClasspath] } + configurations = project.configurations.named('testRuntimeClasspath').map { [it] } } dependencies { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index aa9014eec..f2c76ff1d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -479,7 +479,7 @@ class JavaPluginTest : BasePluginTest() { description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } - configurations = provider { [project.configurations.testRuntimeClasspath] } + configurations = project.configurations.named('testRuntimeClasspath').map { [it] } manifest { attributes '$mainClassAttributeKey': 'my.Main' } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index a87c23710..f907c404b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -152,7 +152,7 @@ class PublishingTest : BasePluginTest() { description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } - configurations = provider { [project.configurations.testRuntimeClasspath] } + configurations = project.configurations.named('testRuntimeClasspath').map { [it] } } """.trimIndent(), dependenciesBlock = """ From c508542850b1a57b271294899777ff2fe0fa88b3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 24 Mar 2025 04:50:57 -0400 Subject: [PATCH 373/941] Update release links in changelog (#1362) --- docs/changes/README.md | 51 +++++++++++++++++------------------------- 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 3ae9434b2..ea87551f5 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,6 +2,7 @@ ## [Unreleased] +[Unreleased]: https://github.com/GradleUp/shadow/compare/9.0.0-beta11...HEAD **Changed** @@ -9,6 +10,7 @@ ## [v9.0.0-beta11] (2025-03-18) +[v9.0.0-beta11]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta11 **Added** @@ -35,6 +37,7 @@ ## [v9.0.0-beta10] (2025-03-05) +[v9.0.0-beta10]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta10 **Added** @@ -58,6 +61,7 @@ ## [v9.0.0-beta9] (2025-02-24) +[v9.0.0-beta9]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta9 **Added** @@ -83,6 +87,7 @@ ## [v9.0.0-beta8] (2025-02-08) +[v9.0.0-beta8]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8 **Added** @@ -101,6 +106,7 @@ ## [v9.0.0-beta7] (2025-02-02) +[v9.0.0-beta7]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta7 **Added** @@ -126,6 +132,7 @@ ## [v9.0.0-beta6] (2025-01-23) +[v9.0.0-beta6]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta6 **Added** @@ -137,6 +144,7 @@ ## [v9.0.0-beta5] (2025-01-21) +[v9.0.0-beta5]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta5 **Added** @@ -157,6 +165,7 @@ ## [v9.0.0-beta4] (2024-12-06) +[v9.0.0-beta4]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta4 **Changed** @@ -169,6 +178,7 @@ ## [v9.0.0-beta2] (2024-11-28) +[v9.0.0-beta2]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta2 **Fixed** @@ -177,6 +187,7 @@ ## [v9.0.0-beta1] (2024-11-27) +[v9.0.0-beta1]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta1 **Added** @@ -201,6 +212,7 @@ ## [v8.3.6] (2025-02-02) +[v8.3.6]: https://github.com/GradleUp/shadow/releases/tag/8.3.6 **Added** @@ -208,6 +220,7 @@ ## [v8.3.5] (2024-11-03) +[v8.3.5]: https://github.com/GradleUp/shadow/releases/tag/8.3.5 **Fixed** @@ -216,12 +229,14 @@ ## [v8.3.4] (2024-10-29) +[v8.3.4]: https://github.com/GradleUp/shadow/releases/tag/8.3.4 **Fixed** - Apply legacy plugin last, and declare capabilities for old plugins, fixes [#964](https://github.com/GradleUp/shadow/issues/964). ([#991](https://github.com/GradleUp/shadow/pull/991)) ## [v8.3.3] (2024-10-02) +[v8.3.3]: https://github.com/GradleUp/shadow/releases/tag/8.3.3 **Changed** @@ -229,6 +244,7 @@ ## [v8.3.2] (2024-09-18) +[v8.3.2]: https://github.com/GradleUp/shadow/releases/tag/8.3.2 **Added** @@ -245,6 +261,7 @@ ## [v8.3.1] (2024-09-10) +[v8.3.1]: https://github.com/GradleUp/shadow/releases/tag/8.3.1 **Added** @@ -257,8 +274,8 @@ - Refix excluding Gradle APIs for java-gradle-plugin. ([#948](https://github.com/GradleUp/shadow/pull/948)) - ## [v8.3.0] (2024-08-08) +[v8.3.0]: https://github.com/GradleUp/shadow/releases/tag/8.3.0 **Changed** @@ -277,12 +294,13 @@ ## [v8.1.1] (2023-03-20) +[v8.1.1]: https://github.com/GradleUp/shadow/releases/tag/8.1.1 **NOTE: ** As of this version, the Github repository has migrated to the `main` branch as the default branch for releases. -[Release Notes](https://github.com/GradleUp/shadow/releases/tag/8.1.1) ## [v8.1.0] (2023-02-26) +[v8.1.0]: https://github.com/GradleUp/shadow/releases/tag/8.1.0 **BREAKING CHANGE:** Due to adoption of the latest version of the `com.gradle.plugin-publish` plugin, the maven GAV coordinates have changed as of this version. The correct coordinates now align with the plugin ID itself: `group=com.github.johnrengelman, artifact=shadow, version=`. @@ -291,12 +309,10 @@ For example, `classpath("com.github.johnrengelman:shadow:8.1.0")` is the correct **BREAKING CHANGE:** The `ConfigureShadowRelocation` task was removed as of this version to better support Gradle configuration caching. Instead, use the `enableRelocation = true` and `relocationPrefix = ""` settings on the `ShadowJar` task type. -[Release Notes](https://github.com/GradleUp/shadow/releases/tag/8.1.0) - ## [v8.0.0] (2023-02-24) +[v8.0.0]: https://github.com/GradleUp/shadow/releases/tag/8.0.0 -[Release Notes](https://github.com/GradleUp/shadow/releases/tag/8.0.0) ## v7.1.3 (unreleased) @@ -613,28 +629,3 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Mon, 24 Mar 2025 05:25:22 -0400 Subject: [PATCH 374/941] Append release notes for 8.x versions (#1363) --- docs/changes/README.md | 61 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ea87551f5..19a5b03c4 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -296,7 +296,23 @@ ## [v8.1.1] (2023-03-20) [v8.1.1]: https://github.com/GradleUp/shadow/releases/tag/8.1.1 -**NOTE: ** As of this version, the Github repository has migrated to the `main` branch as the default branch for releases. +**NOTE:** As of this version, the GitHub repository has migrated to the `main` branch as the default branch for releases. + +## What's Changed +* Replace deprecated ConfigureUtil by [@Goooler](https://github.com/Goooler) in [#826](https://github.com/GradleUp/shadow/pull/826) +* Polish outdated configs by [@Goooler](https://github.com/Goooler) in [#831](https://github.com/GradleUp/shadow/pull/831) +* Update plugin com.gradle.enterprise to v3.12.5 by [@renovate](https://github.com/renovate) in [#838](https://github.com/GradleUp/shadow/pull/838) +* Update dependency gradle to v8.0.2 by [@renovate](https://github.com/renovate) in [#844](https://github.com/GradleUp/shadow/pull/844) +* fix(deps): update dependency org.codehaus.plexus:plexus-utils to v3.5.1 by [@renovate](https://github.com/renovate) in [#837](https://github.com/GradleUp/shadow/pull/837) +* chore(deps): update dependency prismjs to v1.27.0 [security] by [@renovate](https://github.com/renovate) in [#828](https://github.com/GradleUp/shadow/pull/828) +* Encode transformed properties files with specified Charset by [@scottsteen](https://github.com/scottsteen) in [#819](https://github.com/GradleUp/shadow/pull/819) +* chore(deps): update dependency vuepress to v1.9.9 by [@renovate](https://github.com/renovate) in [#842](https://github.com/GradleUp/shadow/pull/842) + +## New Contributors +* [@renovate](https://github.com/renovate) made their first contribution in [#838](https://github.com/GradleUp/shadow/pull/838) +* [@scottsteen](https://github.com/scottsteen) made their first contribution in [#819](https://github.com/GradleUp/shadow/pull/819) + +**Full Changelog**: [`8.1.0...8.1.1`](https://github.com/GradleUp/shadow/compare/8.1.0...8.1.1) ## [v8.1.0] (2023-02-26) @@ -309,10 +325,53 @@ For example, `classpath("com.github.johnrengelman:shadow:8.1.0")` is the correct **BREAKING CHANGE:** The `ConfigureShadowRelocation` task was removed as of this version to better support Gradle configuration caching. Instead, use the `enableRelocation = true` and `relocationPrefix = ""` settings on the `ShadowJar` task type. +## What's Changed +* Minor cleanups by [@Goooler](https://github.com/Goooler) in [#823](https://github.com/GradleUp/shadow/pull/823) +* Support config cache by [@Goooler](https://github.com/Goooler) in [#824](https://github.com/GradleUp/shadow/pull/824) +* Fix RelocatorRemapper: do not map inner class name if not changed by [@Him188](https://github.com/Him188) in [#793](https://github.com/GradleUp/shadow/pull/793) + +## New Contributors +* [@Him188](https://github.com/Him188) made their first contribution in [#793](https://github.com/GradleUp/shadow/pull/793) + +**Full Changelog**: [`8.0.0...8.1.0`](https://github.com/GradleUp/shadow/compare/8.0.0...8.1.0) + ## [v8.0.0] (2023-02-24) [v8.0.0]: https://github.com/GradleUp/shadow/releases/tag/8.0.0 +## What's Changed +* Fix the plugin dependency identifier in the docs by [@lnhrdt](https://github.com/lnhrdt) in [#754](https://github.com/GradleUp/shadow/pull/754) +* mergeGroovyExtensionModules() not working with Groovy 2.5+ by [@paulk-asert](https://github.com/paulk-asert) in [#779](https://github.com/GradleUp/shadow/pull/779) +* Upgrade to ASM 9.3 to support JDK 19. by [@vyazelenko](https://github.com/vyazelenko) in [#770](https://github.com/GradleUp/shadow/pull/770) +* Do not add a dependencies block if it's already there by [@desiderantes](https://github.com/desiderantes) in [#769](https://github.com/GradleUp/shadow/pull/769) +* Update README with new badge and links by [@ThexXTURBOXx](https://github.com/ThexXTURBOXx) in [#743](https://github.com/GradleUp/shadow/pull/743) +* Fix value not set when rawString is true. by [@qian0817](https://github.com/qian0817) in [#765](https://github.com/GradleUp/shadow/pull/765) +* Mark the Log4j2PluginsCacheFileTransformer as cacheable. by [@staktrace](https://github.com/staktrace) in [#724](https://github.com/GradleUp/shadow/pull/724) +* Fix retrieval of dependencies node when publishing by [@netomi](https://github.com/netomi) in [#798](https://github.com/GradleUp/shadow/pull/798) +* Upgrade dependency ASM from `9.3` to `9.4` by [@codecholeric](https://github.com/codecholeric) in [#817](https://github.com/GradleUp/shadow/pull/817) +* Fix a typo of code comment in the minimizing page by [@jebnix](https://github.com/jebnix) in [#800](https://github.com/GradleUp/shadow/pull/800) +* Prefer using plugin extensions over deprecated conventions by [@eskatos](https://github.com/eskatos) in [#821](https://github.com/GradleUp/shadow/pull/821) +* Introduce CleanProperties by [@simPod](https://github.com/simPod) in [#622](https://github.com/GradleUp/shadow/pull/622) +* Support Gradle 8.0 by [@Goooler](https://github.com/Goooler) in [#822](https://github.com/GradleUp/shadow/pull/822) +* Updated dependencies, Gradle versions and Fix Test by [@ElisaMin](https://github.com/ElisaMin) in [#791](https://github.com/GradleUp/shadow/pull/791) + +## New Contributors +* [@lnhrdt](https://github.com/lnhrdt) made their first contribution in [#754](https://github.com/GradleUp/shadow/pull/754) +* [@paulk-asert](https://github.com/paulk-asert) made their first contribution in [#779](https://github.com/GradleUp/shadow/pull/779) +* [@desiderantes](https://github.com/desiderantes) made their first contribution in [#769](https://github.com/GradleUp/shadow/pull/769) +* [@ThexXTURBOXx](https://github.com/ThexXTURBOXx) made their first contribution in [#743](https://github.com/GradleUp/shadow/pull/743) +* [@qian0817](https://github.com/qian0817) made their first contribution in [#765](https://github.com/GradleUp/shadow/pull/765) +* [@staktrace](https://github.com/staktrace) made their first contribution in [#724](https://github.com/GradleUp/shadow/pull/724) +* [@netomi](https://github.com/netomi) made their first contribution in [#798](https://github.com/GradleUp/shadow/pull/798) +* [@codecholeric](https://github.com/codecholeric) made their first contribution in [#817](https://github.com/GradleUp/shadow/pull/817) +* [@jebnix](https://github.com/jebnix) made their first contribution in [#800](https://github.com/GradleUp/shadow/pull/800) +* [@eskatos](https://github.com/eskatos) made their first contribution in [#821](https://github.com/GradleUp/shadow/pull/821) +* [@simPod](https://github.com/simPod) made their first contribution in [#622](https://github.com/GradleUp/shadow/pull/622) +* [@Goooler](https://github.com/Goooler) made their first contribution in [#822](https://github.com/GradleUp/shadow/pull/822) +* [@ElisaMin](https://github.com/ElisaMin) made their first contribution in [#791](https://github.com/GradleUp/shadow/pull/791) + +**Full Changelog**: [`7.1.2...8.0.0`](https://github.com/GradleUp/shadow/compare/7.1.2...8.0.0) + ## v7.1.3 (unreleased) From 5f550bf5ed7b65333a73dd3fa2df4133c9d7a5dc Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 24 Mar 2025 17:31:29 +0800 Subject: [PATCH 375/941] Use h3 for sections --- docs/changes/README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 19a5b03c4..26086b634 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -298,7 +298,7 @@ **NOTE:** As of this version, the GitHub repository has migrated to the `main` branch as the default branch for releases. -## What's Changed +### What's Changed * Replace deprecated ConfigureUtil by [@Goooler](https://github.com/Goooler) in [#826](https://github.com/GradleUp/shadow/pull/826) * Polish outdated configs by [@Goooler](https://github.com/Goooler) in [#831](https://github.com/GradleUp/shadow/pull/831) * Update plugin com.gradle.enterprise to v3.12.5 by [@renovate](https://github.com/renovate) in [#838](https://github.com/GradleUp/shadow/pull/838) @@ -308,7 +308,7 @@ * Encode transformed properties files with specified Charset by [@scottsteen](https://github.com/scottsteen) in [#819](https://github.com/GradleUp/shadow/pull/819) * chore(deps): update dependency vuepress to v1.9.9 by [@renovate](https://github.com/renovate) in [#842](https://github.com/GradleUp/shadow/pull/842) -## New Contributors +### New Contributors * [@renovate](https://github.com/renovate) made their first contribution in [#838](https://github.com/GradleUp/shadow/pull/838) * [@scottsteen](https://github.com/scottsteen) made their first contribution in [#819](https://github.com/GradleUp/shadow/pull/819) @@ -325,12 +325,12 @@ For example, `classpath("com.github.johnrengelman:shadow:8.1.0")` is the correct **BREAKING CHANGE:** The `ConfigureShadowRelocation` task was removed as of this version to better support Gradle configuration caching. Instead, use the `enableRelocation = true` and `relocationPrefix = ""` settings on the `ShadowJar` task type. -## What's Changed +### What's Changed * Minor cleanups by [@Goooler](https://github.com/Goooler) in [#823](https://github.com/GradleUp/shadow/pull/823) * Support config cache by [@Goooler](https://github.com/Goooler) in [#824](https://github.com/GradleUp/shadow/pull/824) * Fix RelocatorRemapper: do not map inner class name if not changed by [@Him188](https://github.com/Him188) in [#793](https://github.com/GradleUp/shadow/pull/793) -## New Contributors +### New Contributors * [@Him188](https://github.com/Him188) made their first contribution in [#793](https://github.com/GradleUp/shadow/pull/793) **Full Changelog**: [`8.0.0...8.1.0`](https://github.com/GradleUp/shadow/compare/8.0.0...8.1.0) @@ -339,7 +339,7 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Tue, 25 Mar 2025 07:41:41 +0800 Subject: [PATCH 376/941] Update plugin android-lint to v8.9.1 (#1364) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 26adf606a..502368ee8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,6 +32,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.9.0" +android-lint = "com.android.lint:8.9.1" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" spotless = "com.diffplug.spotless:7.0.2" From 065016b5f60fa109d26df994ed1334a12bae8add Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 24 Mar 2025 23:40:11 -0400 Subject: [PATCH 377/941] Rename MinimizeTest to MinimizationTest (#1367) Aligns with `RelocationTest`. --- .../plugins/shadow/{MinimizeTest.kt => MinimizationTest.kt} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{MinimizeTest.kt => MinimizationTest.kt} (99%) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt similarity index 99% rename from src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt index 62d67a8d5..2576bc250 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt @@ -9,7 +9,7 @@ import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test -class MinimizeTest : BasePluginTest() { +class MinimizationTest : BasePluginTest() { /** * 'api' used as api for 'impl', and depended on 'lib'. 'junit' is independent. * The minimize step shall remove 'junit', but not 'api'. From b2a0ca4709e57558c0bba90ae8bf5af80bc3823d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Mar 2025 01:00:22 -0400 Subject: [PATCH 378/941] Append Kdoc for ShadowJar and minor cleanups (#1368) * Use with in `applyPlugin` * Don't use ShadowBasePlugin.SHADOW in prefixProvider * Check more properties in applyPlugin * Add KDoc for ShadowJar --- .../gradle/plugins/shadow/JavaPluginTest.kt | 32 +++++++-- .../gradle/plugins/shadow/RelocationTest.kt | 3 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 72 ++++++++++++++++++- 3 files changed, 98 insertions(+), 9 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index f2c76ff1d..0402d96d6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -4,7 +4,9 @@ import assertk.all import assertk.assertThat import assertk.assertions.contains import assertk.assertions.containsMatch +import assertk.assertions.containsOnly import assertk.assertions.isEqualTo +import assertk.assertions.isFalse import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isNotEqualTo @@ -16,6 +18,7 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHA import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey +import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.util.Issue @@ -57,12 +60,29 @@ class JavaPluginTest : BasePluginTest() { val shadowTask = project.tasks.getByName(SHADOW_JAR_TASK_NAME) as ShadowJar val shadowConfig = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) - assertThat(shadowTask.archiveBaseName.get()).isEqualTo(projectName) - assertThat(shadowTask.destinationDirectory.get().asFile) - .isEqualTo(project.layout.buildDirectory.dir("libs").get().asFile) - assertThat(shadowTask.archiveVersion.get()).isEqualTo(version) - assertThat(shadowTask.archiveClassifier.get()).isEqualTo("all") - assertThat(shadowTask.archiveExtension.get()).isEqualTo("jar") + with(shadowTask) { + assertThat(archiveBaseName.get()).isEqualTo(projectName) + assertThat(destinationDirectory.get().asFile) + .isEqualTo(project.layout.buildDirectory.dir("libs").get().asFile) + assertThat(archiveVersion.get()).isEqualTo(version) + assertThat(archiveClassifier.get()).isEqualTo("all") + assertThat(archiveExtension.get()).isEqualTo("jar") + assertThat(archiveFileName.get()).isEqualTo("my-shadow-1.0.0-all.jar") + assertThat(archiveFile.get().asFile).all { + isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) + isEqualTo(project.projectDir.resolve("build/libs/my-shadow-1.0.0-all.jar")) + } + assertThat(archiveAppendix.orNull).isNull() + + assertThat(minimizeJar.get()).isFalse() + assertThat(enableRelocation.get()).isFalse() + assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) + assertThat(configurations.get()).all { + isNotEmpty() + containsOnly(project.runtimeConfiguration) + } + } + assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 54305a794..52b3a9a34 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -551,8 +551,7 @@ class RelocationTest : BasePluginTest() { private companion object { @JvmStatic fun prefixProvider() = listOf( - // The default values. - Arguments.of(ShadowBasePlugin.SHADOW), + Arguments.of("foo"), Arguments.of("new.pkg"), Arguments.of("new/path"), ) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 3e45c0142..617cd0c83 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -102,12 +102,23 @@ public abstract class ShadowJar : } } + /** + * [ResourceTransformer]s to be applied in the shadow steps. + */ @get:Nested public open val transformers: SetProperty = objectFactory.setProperty() + /** + * [Relocator]s to be applied in the shadow steps. + */ @get:Nested public open val relocators: SetProperty = objectFactory.setProperty() + /** + * The configurations to include dependencies from. + * + * Defaults to a set that contains `runtimeClasspath` or `runtime` configuration. + */ @get:Classpath @get:Optional public open val configurations: SetProperty = objectFactory.setProperty() @@ -116,6 +127,9 @@ public abstract class ShadowJar : public open val dependencyFilter: Property = objectFactory.property(DefaultDependencyFilter(project)) + /** + * Final dependencies to be shadowed. + */ @get:Classpath public open val includedDependencies: ConfigurableFileCollection = objectFactory.fileCollection { dependencyFilter.zip(configurations) { df, cs -> df.resolve(cs) } @@ -132,7 +146,7 @@ public abstract class ShadowJar : /** * Prefix to use for relocated packages. * - * Defaults to [ShadowBasePlugin.SHADOW]. + * Defaults to `shadow`. */ @get:Input public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) @@ -146,33 +160,54 @@ public abstract class ShadowJar : @Input // Trigger task executions after excludes changed. override fun getExcludes(): MutableSet = super.getExcludes() + /** + * Enable [minimizeJar], this equals to `minimizeJar.set(true)`. + */ override fun minimize() { minimizeJar.set(true) } + /** + * Enable [minimizeJar] and execute the [action] with the [DependencyFilter] for minimize. + */ override fun minimize(action: Action?) { minimize() action?.execute(dependencyFilterForMinimize) } + /** + * Extra dependency operations to be applied in the shadow steps. + */ override fun dependencies(action: Action?) { action?.execute(dependencyFilter.get()) } + /** + * Merge Java services files. + */ override fun mergeServiceFiles() { transform(ServiceFileTransformer::class.java, null) } + /** + * Merge Java services files with [rootPath]. + */ override fun mergeServiceFiles(rootPath: String) { transform(ServiceFileTransformer::class.java) { it.path = rootPath } } + /** + * Merge Java services files with [action]. + */ override fun mergeServiceFiles(action: Action?) { transform(ServiceFileTransformer::class.java, action) } + /** + * Merge Groovy extension modules (`META-INF/**/org.codehaus.groovy.runtime.ExtensionModule`). + */ override fun mergeGroovyExtensionModules() { transform(GroovyExtensionModuleTransformer::class.java, null) } @@ -193,6 +228,9 @@ public abstract class ShadowJar : } } + /** + * Relocate classes matching [pattern] to [destination] using [SimpleRelocator]. + */ override fun relocate( pattern: String, destination: String, @@ -202,35 +240,67 @@ public abstract class ShadowJar : addRelocator(relocator, action) } + /** + * Relocate classes using a [Relocator]. + */ override fun relocate(clazz: Class, action: Action?) { val relocator = clazz.getDeclaredConstructor().newInstance() addRelocator(relocator, action) } + /** + * Relocate classes using a [Relocator]. + */ override fun relocate(relocator: R, action: Action?) { addRelocator(relocator, action) } + /** + * Relocate classes using a [Relocator]. + * + * This is a convenience method for [relocate] with a reified type parameter for Kotlin. + */ public inline fun relocate() { relocate(R::class.java, null) } + /** + * Relocate classes using a [Relocator]. + * + * This is a convenience method for [relocate] with a reified type parameter for Kotlin. + */ public inline fun relocate(action: Action?) { relocate(R::class.java, action) } + /** + * Transform resources using a [ResourceTransformer]. + */ override fun transform(clazz: Class, action: Action?) { addTransform(clazz.create(objectFactory), action) } + /** + * Transform resources using a [ResourceTransformer]. + */ override fun transform(transformer: T, action: Action?) { addTransform(transformer, action) } + /** + * Transform resources using a [ResourceTransformer]. + * + * This is a convenience method for [transform] with a reified type parameter for Kotlin. + */ public inline fun transform() { transform(T::class.java, null) } + /** + * Transform resources using a [ResourceTransformer]. + * + * This is a convenience method for [transform] with a reified type parameter for Kotlin. + */ public inline fun transform(action: Action?) { transform(T::class.java, action) } From 23fb01c7995cc70c0703f31425999521dd3b30b9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Mar 2025 01:33:48 -0400 Subject: [PATCH 379/941] Support command line options for ShadowJar (#1365) * Annotate minimizeJar, enableRelocation, and relocationPrefix as `Option`. * Test `enableMinimizationByCliOption` * Test `enableRelocationByCliOption` * Test `shadowJarCliOptions` * Update changelog * Update docs/getting-started/README.md --- docs/changes/README.md | 14 +++++ docs/getting-started/README.md | 15 ++++++ .../gradle/plugins/shadow/JavaPluginTest.kt | 13 +++++ .../gradle/plugins/shadow/MinimizationTest.kt | 33 ++++++++++++ .../gradle/plugins/shadow/RelocationTest.kt | 53 +++++++++++++++++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 4 ++ 6 files changed, 132 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index 26086b634..5ee10af14 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -4,6 +4,20 @@ ## [Unreleased] [Unreleased]: https://github.com/GradleUp/shadow/compare/9.0.0-beta11...HEAD +**Added** + +- Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) +``` +Options: + +--enable-relocation Enable relocation of packages in the jar +--no-enable-relocation Disables option --enable-relocation +--minimize-jar Minimize the jar by removing unused classes +--no-minimize-jar Disables option --minimize-jar +--relocation-prefix Prefix to use for relocated packages +--rerun Causes the task to be re-run even if up-to-date +``` + **Changed** - Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index b9dbd48ce..c34f3b13b 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -140,3 +140,18 @@ Shadow will automatically configure the following behavior: * `META-INF/versions/**/module-info.class` * `module-info.class` * Creates and registers the `shadow` component in the project (used for integrating with `maven-publish`). + +## ShadowJar Command Line options + +Sometimes, a user wants to declare the value of an exposed task property on the command line instead of the +build script. Passing property values on the command line is particularly helpful if they change more frequently. +Here are the options that can be passed to the `shadowJar`: + +``` +--enable-relocation Enable relocation of packages in the jar +--no-enable-relocation Disables option --enable-relocation +--minimize-jar Minimize the jar by removing unused classes +--no-minimize-jar Disables option --minimize-jar +--relocation-prefix Prefix to use for relocated packages +--rerun Causes the task to be re-run even if up-to-date +``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 0402d96d6..1a5072d45 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -120,6 +120,19 @@ class JavaPluginTest : BasePluginTest() { } } + @Test + fun shadowJarCliOptions() { + val result = run("help", "--task", shadowJarTask) + + assertThat(result.output).contains( + "--enable-relocation Enable relocation of packages in the jar", + "--no-enable-relocation Disables option --enable-relocation", + "--minimize-jar Minimize the jar by removing unused classes", + " --no-minimize-jar Disables option --minimize-jar", + "--relocation-prefix Prefix to use for relocated packages", + ) + } + @Test fun includeProjectDependencies() { writeClientAndServerModules() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt index 2576bc250..c53439244 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt @@ -8,6 +8,8 @@ import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class MinimizationTest : BasePluginTest() { /** @@ -204,6 +206,37 @@ class MinimizationTest : BasePluginTest() { } } + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun enableMinimizationByCliOption(enable: Boolean) { + writeClientAndServerModules() + + if (enable) { + run(serverShadowJarTask, "--minimize-jar") + } else { + run(serverShadowJarTask) + } + + assertThat(outputServerShadowJar).useAll { + if (enable) { + containsAtLeast( + "server/Server.class", + manifestEntry, + ) + containsNone( + "client/Client.class", + ) + } else { + containsAtLeast( + "client/Client.class", + "server/Server.class", + *junitEntries, + manifestEntry, + ) + } + } + } + private fun writeApiLibAndImplModules() { settingsScriptPath.appendText( """ diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 52b3a9a34..d8d570883 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains +import assertk.assertions.containsOnly import assertk.assertions.isEmpty import assertk.assertions.isInstanceOf import assertk.assertions.isNotEmpty @@ -548,6 +549,50 @@ class RelocationTest : BasePluginTest() { } } + @ParameterizedTest + @MethodSource("relocationCliOptionProvider") + fun enableRelocationByCliOption(enableRelocation: Boolean, relocationPrefix: String) { + val mainClassEntry = writeClass() + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent(), + ) + val relocatedEntries = junitEntries.map { "$relocationPrefix/$it" } + .filterNot { it.startsWith("$relocationPrefix/META-INF/") } + .toTypedArray() + + if (enableRelocation) { + run(shadowJarTask, "--enable-relocation", "--relocation-prefix=$relocationPrefix") + } else { + run(shadowJarTask, "--relocation-prefix=$relocationPrefix") + } + + assertThat(outputShadowJar).useAll { + if (enableRelocation) { + containsAtLeast( + mainClassEntry, + *relocatedEntries, + manifestEntry, + ) + containsNone( + *junitEntries.filterNot { it.startsWith("META-INF/") }.toTypedArray(), + ) + } else { + containsAtLeast( + mainClassEntry, + *junitEntries, + manifestEntry, + ) + containsNone( + *relocatedEntries.filterNot { it.startsWith("META-INF/") }.toTypedArray(), + ) + } + } + } + private companion object { @JvmStatic fun prefixProvider() = listOf( @@ -563,5 +608,13 @@ class RelocationTest : BasePluginTest() { Arguments.of(false, true), Arguments.of(true, true), ) + + @JvmStatic + fun relocationCliOptionProvider() = listOf( + Arguments.of(false, "foo"), + Arguments.of(false, "bar"), + Arguments.of(true, "foo"), + Arguments.of(true, "bar"), + ) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 617cd0c83..2d0db0746 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -46,6 +46,7 @@ import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.bundling.ZipEntryCompression +import org.gradle.api.tasks.options.Option @CacheableTask public abstract class ShadowJar : @@ -74,6 +75,7 @@ public abstract class ShadowJar : * Defaults to `false`. */ @get:Input + @get:Option(option = "minimize-jar", description = "Minimize the jar by removing unused classes") public open val minimizeJar: Property = objectFactory.property(false) @get:Classpath @@ -141,6 +143,7 @@ public abstract class ShadowJar : * Defaults to `false`. */ @get:Input + @get:Option(option = "enable-relocation", description = "Enable relocation of packages in the jar") public open val enableRelocation: Property = objectFactory.property(false) /** @@ -149,6 +152,7 @@ public abstract class ShadowJar : * Defaults to `shadow`. */ @get:Input + @get:Option(option = "relocation-prefix", description = "Prefix to use for relocated packages") public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) @Internal From be38cf7b374a81e3efead3d33bfde10244c71a61 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Mar 2025 05:11:31 -0400 Subject: [PATCH 380/941] See more info by listing command line options (#1369) https://docs.gradle.org/current/userguide/custom_tasks.html#sec:listing_task_options --- docs/getting-started/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index c34f3b13b..26005f93d 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -155,3 +155,11 @@ Here are the options that can be passed to the `shadowJar`: --relocation-prefix Prefix to use for relocated packages --rerun Causes the task to be re-run even if up-to-date ``` + +Also, you can view more information about the `shadowJar` task by running the following command: + +```sh +./gradlew -q help --task shadowJar +``` + +Refer to [listing command line options](https://docs.gradle.org/current/userguide/custom_tasks.html#sec:listing_task_options). From 4fbaea4e8fbe7aace05ab2874f550660b2aaa656 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Mar 2025 05:21:57 -0400 Subject: [PATCH 381/941] Correct the Kdoc for relocate (#1370) --- .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 2d0db0746..2e9320734 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -233,7 +233,7 @@ public abstract class ShadowJar : } /** - * Relocate classes matching [pattern] to [destination] using [SimpleRelocator]. + * Relocate classes and resources matching [pattern] to [destination] using [SimpleRelocator]. */ override fun relocate( pattern: String, @@ -245,7 +245,7 @@ public abstract class ShadowJar : } /** - * Relocate classes using a [Relocator]. + * Relocate classes and resources using a [Relocator]. */ override fun relocate(clazz: Class, action: Action?) { val relocator = clazz.getDeclaredConstructor().newInstance() @@ -253,14 +253,14 @@ public abstract class ShadowJar : } /** - * Relocate classes using a [Relocator]. + * Relocate classes and resources using a [Relocator]. */ override fun relocate(relocator: R, action: Action?) { addRelocator(relocator, action) } /** - * Relocate classes using a [Relocator]. + * Relocate classes and resources using a [Relocator]. * * This is a convenience method for [relocate] with a reified type parameter for Kotlin. */ @@ -269,7 +269,7 @@ public abstract class ShadowJar : } /** - * Relocate classes using a [Relocator]. + * Relocate classes and resources using a [Relocator]. * * This is a convenience method for [relocate] with a reified type parameter for Kotlin. */ From acbf40e66f9c044564421e845281c4ce90aeb69e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Mar 2025 10:10:26 -0400 Subject: [PATCH 382/941] Try more containsOnly assertions (#1366) --- .../gradle/plugins/shadow/BasePluginTest.kt | 1 + .../gradle/plugins/shadow/FilteringTest.kt | 28 +++--- .../gradle/plugins/shadow/GroovyPluginTest.kt | 6 +- .../gradle/plugins/shadow/JavaPluginTest.kt | 63 +++++++++----- .../gradle/plugins/shadow/MinimizationTest.kt | 27 ++++-- .../gradle/plugins/shadow/PublishingTest.kt | 9 +- .../gradle/plugins/shadow/RelocationTest.kt | 86 ++++++++----------- .../gradle/plugins/shadow/ScalaPluginTest.kt | 7 +- .../shadow/caching/FilteringCachingTest.kt | 29 ++++--- .../shadow/caching/MinimizationCachingTest.kt | 17 ++-- .../shadow/caching/RelocationCachingTest.kt | 16 ++-- .../shadow/caching/ShadowJarCachingTest.kt | 21 +++-- .../shadow/caching/TransformerCachingTest.kt | 23 ++++- .../shadow/transformers/TransformersTest.kt | 6 +- .../gradle/plugins/shadow/util/JarPath.kt | 20 +++-- 15 files changed, 219 insertions(+), 140 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 10779b2a1..84183a432 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 @@ -407,6 +407,7 @@ abstract class BasePluginTest { } val junitEntries: Array = junitRawEntries.map { it.name }.toTypedArray() const val manifestEntry = "META-INF/MANIFEST.MF" + val manifestEntries = arrayOf("META-INF/", manifestEntry) val shadowJar: String = "tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name})" const val runShadow = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index d9e00aed0..020c825c7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -1,8 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.containsNone import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText @@ -35,8 +33,12 @@ class FilteringTest : BasePluginTest() { @Test fun includeAllDependencies() { run(shadowJarTask) + assertThat(outputShadowJar).useAll { - containsAtLeast(*entriesInAB) + containsOnly( + *entriesInAB, + *manifestEntries, + ) } } @@ -56,7 +58,7 @@ class FilteringTest : BasePluginTest() { containsOnly( "a.properties", "b.properties", - manifestEntry, + *manifestEntries, ) } } @@ -117,8 +119,9 @@ class FilteringTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsOnly( "d.properties", + "my/", "my/Passed.class", - manifestEntry, + *manifestEntries, ) } } @@ -138,12 +141,11 @@ class FilteringTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsAtLeast( + containsOnly( + "server/", "server/Server.class", *junitEntries, - ) - containsNone( - "client/Client.class", + *manifestEntries, ) } } @@ -162,9 +164,11 @@ class FilteringTest : BasePluginTest() { assertThat(outputServerShadowJar).useAll { containsOnly( + "client/", + "server/", "client/Client.class", "server/Server.class", - manifestEntry, + *manifestEntries, ) } } @@ -187,7 +191,7 @@ class FilteringTest : BasePluginTest() { containsOnly( "a.properties", "b.properties", - manifestEntry, + *manifestEntries, ) } } @@ -232,7 +236,7 @@ class FilteringTest : BasePluginTest() { containsOnly( "c.properties", *entriesInAB, - manifestEntry, + *manifestEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt index 24381a783..6aacb7555 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt @@ -2,7 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeEach @@ -35,9 +35,11 @@ class GroovyPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast( + containsOnly( + "my/", mainClassEntry, *junitEntries, + *manifestEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 1a5072d45..ca13399bc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -106,9 +106,11 @@ class JavaPluginTest : BasePluginTest() { } assertThat(outputShadowJar).useAll { - containsAtLeast( + containsOnly( + "my/", mainClassEntry, *junitEntries, + *manifestEntries, ) } } @@ -140,10 +142,13 @@ class JavaPluginTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsAtLeast( + containsOnly( + "client/", + "server/", "client/Client.class", "server/Server.class", *junitEntries, + *manifestEntries, ) } } @@ -156,15 +161,20 @@ class JavaPluginTest : BasePluginTest() { assertThat(jarPath("server/build/libs/server-1.0.jar")).useAll { containsOnly( + "server/", "server/Server.class", - manifestEntry, + *manifestEntries, ) } assertThat(jarPath("client/build/libs/client-1.0-all.jar")).useAll { containsAtLeast( + "client/", "client/Client.class", "client/junit/framework/Test.class", ) + containsNone( + "server/Server.class", + ) } } @@ -177,13 +187,14 @@ class JavaPluginTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsAtLeast( + containsOnly( + "client/", + "server/", + "client/junit/", "client/Client.class", "server/Server.class", *shadowedEntries, - ) - containsNone( - *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), + *manifestEntries, ) } assertThat(jarPath("client/build/libs/client-1.0-all.jar")).useAll { @@ -191,6 +202,9 @@ class JavaPluginTest : BasePluginTest() { "client/Client.class", "client/junit/framework/Test.class", ) + containsNone( + "server/Server.class", + ) } } @@ -256,10 +270,11 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsOnly( + "my/", "my/Passed.class", "a.properties", "META-INF/a.properties", - manifestEntry, + *manifestEntries, ) } } @@ -280,7 +295,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsOnly( *entriesInA, - manifestEntry, + *manifestEntries, ) } } @@ -320,11 +335,12 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast( + containsOnly( "api.properties", "implementation.properties", "runtimeOnly.properties", "implementation-dep.properties", + *manifestEntries, ) } } @@ -345,7 +361,7 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsOnly( *entriesInA, - manifestEntry, + *manifestEntries, ) } } @@ -487,10 +503,13 @@ class JavaPluginTest : BasePluginTest() { getMainAttr(classPathAttributeKey).isNull() containsOnly( + "my/", + "my/plugin/", "my/plugin/MyPlugin.class", + "META-INF/gradle-plugins/", "META-INF/gradle-plugins/my.plugin.properties", *entriesInA, - manifestEntry, + *manifestEntries, ) } } @@ -523,9 +542,11 @@ class JavaPluginTest : BasePluginTest() { run(testShadowJarTask) assertThat(jarPath("build/libs/my-1.0-tests.jar")).useAll { - containsAtLeast( + containsOnly( + "my/", mainClassEntry, *junitEntries, + *manifestEntries, ) getMainAttr(mainClassAttributeKey).isNotNull() } @@ -577,9 +598,11 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(jarPath("build/libs/my-shadow.tar")).useAll { - containsAtLeast( + containsOnly( + "my/", mainClassEntry, *junitEntries, + *manifestEntries, ) } } @@ -635,13 +658,11 @@ class JavaPluginTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsOnly( "my/", - mainClassEntry, "Bar/", "Bar/Foo", - "META-INF/", "META-INF/a-1.0.jar", - manifestEntry, - includeDirs = true, + mainClassEntry, + *manifestEntries, ) getContent("Bar/Foo").isEqualTo("Foo") } @@ -685,9 +706,11 @@ class JavaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast( - mainClassEntry, + containsOnly( "module-info.class", + "my/", + mainClassEntry, + *manifestEntries, ) getContent("module-info.class").all { isNotEmpty() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt index c53439244..6813ee539 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt @@ -25,14 +25,14 @@ class MinimizationTest : BasePluginTest() { assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { containsAtLeast( + "api/", + "lib/", + "impl/", "impl/SimpleEntity.class", "api/Entity.class", "api/UnusedEntity.class", "lib/LibEntity.class", - ) - containsNone( - "junit/framework/Test.class", - "lib/UnusedLibEntity.class", + *manifestEntries, ) } } @@ -59,12 +59,15 @@ class MinimizationTest : BasePluginTest() { assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { containsOnly( + "api/", + "impl/", + "lib/", "impl/SimpleEntity.class", "api/Entity.class", "api/UnusedEntity.class", "lib/LibEntity.class", "lib/UnusedLibEntity.class", - manifestEntry, + *manifestEntries, ) } } @@ -149,9 +152,13 @@ class MinimizationTest : BasePluginTest() { run(serverShadowJarTask) assertThat(outputServerShadowJar).useAll { - containsAtLeast( + containsOnly( + "client/", + "server/", "client/Client.class", "server/Server.class", + *junitEntries, + *manifestEntries, ) } } @@ -221,17 +228,19 @@ class MinimizationTest : BasePluginTest() { if (enable) { containsAtLeast( "server/Server.class", - manifestEntry, + *manifestEntries, ) containsNone( "client/Client.class", ) } else { - containsAtLeast( + containsOnly( + "client/", + "server/", "client/Client.class", "server/Server.class", *junitEntries, - manifestEntry, + *manifestEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index f907c404b..2c907d66b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -169,7 +169,10 @@ class PublishingTest : BasePluginTest() { publish() assertThat(repoJarPath("my/maven/1.0/maven-1.0-tests.jar")).useAll { - containsAtLeast(*junitEntries) + containsOnly( + *junitEntries, + *manifestEntries, + ) } } @@ -278,7 +281,7 @@ class PublishingTest : BasePluginTest() { containsOnly( "aa.properties", "aa2.properties", - manifestEntry, + *manifestEntries, ) } assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) @@ -368,7 +371,7 @@ class PublishingTest : BasePluginTest() { assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0-all.jar")).useAll { containsOnly( *entriesInAB, - manifestEntry, + *manifestEntries, ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index d8d570883..9bb353cea 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.containsOnly import assertk.assertions.isEmpty import assertk.assertions.isInstanceOf import assertk.assertions.isNotEmpty @@ -11,8 +10,6 @@ import assertk.fail import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.Companion.CONSTANT_TIME_FOR_ZIP_ENTRIES import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.containsNone import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import java.net.URLClassLoader import kotlin.io.path.appendText @@ -61,9 +58,7 @@ class RelocationTest : BasePluginTest() { "my/", mainClassEntry, *shadowedEntries, - "META-INF/", - manifestEntry, - includeDirs = true, + *manifestEntries, ) } // Make sure the relocator count is aligned with the number of unique packages in junit jar. @@ -102,16 +97,13 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast( + containsOnly( + "my/", mainClassEntry, *runnerEntries, *frameworkEntries, *otherJunitEntries, - ) - containsNone( - *junitEntries.filter { it !in otherJunitEntries }.toTypedArray(), - *otherJunitEntries.map { "a/$it" }.toTypedArray(), - *otherJunitEntries.map { "b/$it" }.toTypedArray(), + *manifestEntries, ) } } @@ -147,15 +139,15 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast( + containsOnly( + "b/", + "my/", + "junit/runner/", mainClassEntry, *runnerEntries, *frameworkEntries, *otherJunitEntries, - ) - containsNone( - *otherJunitEntries.map { "a/$it" }.toTypedArray(), - *otherJunitEntries.map { "b/$it" }.toTypedArray(), + *manifestEntries, ) } } @@ -194,12 +186,12 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast( + containsOnly( + "my/", + "shadow/", "my/MyTest.class", *shadowedEntries, - ) - containsNone( - *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), + *manifestEntries, ) } @@ -269,16 +261,17 @@ class RelocationTest : BasePluginTest() { run(":app:$SHADOW_JAR_TASK_NAME") assertThat(jarPath("app/build/libs/app-all.jar")).useAll { - containsAtLeast( + containsOnly( "TEST", "APP-TEST", "test.properties", - "app/core/Core.class", + "app/", + "app/core/", + "app/junit/", "app/App.class", + "app/core/Core.class", *shadowedEntries, - ) - containsNone( - *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), + *manifestEntries, ) } } @@ -317,10 +310,11 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsOnly( + "bar/", "bar/Foo.class", "bar/foo.properties", "bar/dep.properties", - manifestEntry, + *manifestEntries, ) } } @@ -469,8 +463,9 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsOnly( + "kotlin/", "kotlin/kotlin.kotlin_builtins", - manifestEntry, + *manifestEntries, ) } } @@ -503,23 +498,16 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { if (exclude) { - containsAtLeast( + containsOnly( *junitEntries, - manifestEntry, - ) - containsNone( - "foo/$manifestEntry", - *junitEntries.map { "foo/$it" }.toTypedArray(), + *manifestEntries, ) } else { - containsAtLeast( + containsOnly( + "foo/", "foo/$manifestEntry", *junitEntries.map { "foo/$it" }.toTypedArray(), ) - containsNone( - *junitEntries, - manifestEntry, - ) } } } @@ -543,6 +531,9 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { containsOnly( + "foo/", + "foo/my/", + "foo/META-INF/", "foo/$mainClassEntry", "foo/$manifestEntry", ) @@ -572,22 +563,19 @@ class RelocationTest : BasePluginTest() { assertThat(outputShadowJar).useAll { if (enableRelocation) { - containsAtLeast( + containsOnly( + "my/", + "$relocationPrefix/", mainClassEntry, *relocatedEntries, - manifestEntry, - ) - containsNone( - *junitEntries.filterNot { it.startsWith("META-INF/") }.toTypedArray(), + *manifestEntries, ) } else { - containsAtLeast( + containsOnly( + "my/", mainClassEntry, *junitEntries, - manifestEntry, - ) - containsNone( - *relocatedEntries.filterNot { it.startsWith("META-INF/") }.toTypedArray(), + *manifestEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt index ceb200270..a2dca340d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt @@ -2,7 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeEach @@ -35,9 +35,12 @@ class ScalaPluginTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast( + containsOnly( + "my/", + "my/Main$.class", mainClassEntry, *junitEntries, + *manifestEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt index 9d6e29806..5da2cd1cd 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.Assert import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.readText @@ -34,7 +33,7 @@ class FilteringCachingTest : BaseCachingTest() { containsOnly( "d.properties", *entriesInAB, - manifestEntry, + *manifestEntries, ) } } @@ -57,7 +56,7 @@ class FilteringCachingTest : BaseCachingTest() { "b.properties", "c.properties", "d.properties", - manifestEntry, + *manifestEntries, ) } } @@ -79,10 +78,12 @@ class FilteringCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsAtLeast( - *entriesInAB, + containsOnly( + "my/", mainClassEntry, main2ClassEntry, + *entriesInAB, + *manifestEntries, ) } @@ -96,9 +97,10 @@ class FilteringCachingTest : BaseCachingTest() { assertCompositeExecutions { containsOnly( + "my/", mainClassEntry, main2ClassEntry, - manifestEntry, + *manifestEntries, ) } @@ -112,8 +114,9 @@ class FilteringCachingTest : BaseCachingTest() { assertCompositeExecutions { containsOnly( + "my/", mainClassEntry, - manifestEntry, + *manifestEntries, ) } @@ -127,9 +130,10 @@ class FilteringCachingTest : BaseCachingTest() { assertCompositeExecutions { containsOnly( + "my/", mainClassEntry, main2ClassEntry, - manifestEntry, + *manifestEntries, ) } } @@ -146,9 +150,11 @@ class FilteringCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsAtLeast( + containsOnly( + "my/", mainClassEntry, *junitEntries, + *manifestEntries, ) } @@ -164,8 +170,9 @@ class FilteringCachingTest : BaseCachingTest() { assertCompositeExecutions { containsOnly( + "my/", mainClassEntry, - manifestEntry, + *manifestEntries, ) } } @@ -191,7 +198,7 @@ class FilteringCachingTest : BaseCachingTest() { containsOnly( "c.properties", *entriesInAB, - manifestEntry, + *manifestEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt index 73f40cf9e..d7e82b049 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt @@ -1,8 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.caching import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.containsNone +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test @@ -22,10 +21,13 @@ class MinimizationCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsAtLeast( - "server/Server.class", + containsOnly( + "client/", + "server/", "client/Client.class", + "server/Server.class", *junitEntries, + *manifestEntries, ) } @@ -40,12 +42,11 @@ class MinimizationCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsAtLeast( + containsOnly( + "server/", "server/Server.class", *junitEntries, - ) - containsNone( - "client/Client.class", + *manifestEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index 32b97a282..a67ac5bca 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -1,7 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.caching -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.containsNone +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import org.junit.jupiter.api.Test @@ -21,9 +20,11 @@ class RelocationCachingTest : BaseCachingTest() { val mainClassEntry = writeClass(withImports = true) assertCompositeExecutions { - containsAtLeast( + containsOnly( + "my/", mainClassEntry, *junitEntries, + *manifestEntries, ) } @@ -38,12 +39,13 @@ class RelocationCachingTest : BaseCachingTest() { .map { it.replace("junit/framework/", "foo/junit/framework/") }.toTypedArray() assertCompositeExecutions { - containsAtLeast( + containsOnly( + "my/", + "foo/", + "foo/junit/", mainClassEntry, *shadowedEntries, - ) - containsNone( - *junitEntries.filter { it.startsWith("junit/framework/") }.toTypedArray(), + *manifestEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index fafbbb04d..85049a751 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.readText @@ -25,7 +24,10 @@ class ShadowJarCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsAtLeast(*entriesInAB) + containsOnly( + *entriesInAB, + *manifestEntries, + ) } val replaced = projectScriptPath.readText().lines() @@ -36,7 +38,7 @@ class ShadowJarCachingTest : BaseCachingTest() { assertCompositeExecutions { containsOnly( *entriesInA, - manifestEntry, + *manifestEntries, ) } } @@ -52,7 +54,10 @@ class ShadowJarCachingTest : BaseCachingTest() { ) assertCompositeExecutions { - containsAtLeast(*entriesInAB) + containsOnly( + *entriesInAB, + *manifestEntries, + ) } projectScriptPath.appendText( @@ -65,7 +70,10 @@ class ShadowJarCachingTest : BaseCachingTest() { assertExecutionsFromCacheAndUpToDate() assertThat(jarPath("build/libs/foo-1.0-all.jar")).useAll { - containsAtLeast(*entriesInAB) + containsOnly( + *entriesInAB, + *manifestEntries, + ) } } @@ -81,9 +89,10 @@ class ShadowJarCachingTest : BaseCachingTest() { ) val assertions = { assertCompositeExecutions { - containsAtLeast( + containsOnly( "c.properties", "d.properties", + *manifestEntries, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 100b16e42..d53114530 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -17,6 +17,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransform import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.deleteExisting @@ -41,7 +42,11 @@ class TransformerCachingTest : BaseCachingTest() { fun shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer() { val assertions = { assertCompositeExecutions { - containsAtLeast(mainClass) + containsOnly( + "my/", + mainClass, + *manifestEntries, + ) } } @@ -68,7 +73,13 @@ class TransformerCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.properties").writeText("foo=bar") val assertions = { name: String -> assertCompositeExecutions { - containsAtLeast(mainClass, "foo/$name.properties") + containsOnly( + "my/", + "foo/", + "foo/$name.properties", + mainClass, + *manifestEntries, + ) getContent("foo/$name.properties").isEqualTo("foo=$name") } } @@ -98,7 +109,13 @@ class TransformerCachingTest : BaseCachingTest() { path("src/main/resources/foo/bar.xml").writeText("bar") val assertions = { name: String -> assertCompositeExecutions { - containsAtLeast(mainClass, "foo/$name.xml") + containsOnly( + "my/", + "foo/", + "foo/$name.xml", + mainClass, + *manifestEntries, + ) getContent("foo/$name.xml").contains("$name") } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index f7c366936..2ed0f9019 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -10,6 +10,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.getStream import java.util.jar.Attributes as JarAttribute import kotlin.io.path.appendText @@ -121,7 +122,10 @@ class TransformersTest : BaseTransformerTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast(*entriesInAB) + containsOnly( + *entriesInAB, + *manifestEntries, + ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index 6e81379a4..1a23fe8dc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -42,17 +42,23 @@ fun Assert.getContent(entryName: String) = transform { it.getContent(en fun Assert.getMainAttr(name: String) = transform { it.getMainAttr(name) } +/** + * Ensures the JAR contains at least the specified entries. + * Commonly used with [containsNone] to verify additional constraints. + */ fun Assert.containsAtLeast(vararg entries: String) = toEntries().containsAtLeast(*entries) +/** + * Ensures the JAR does not contain any of the specified entries. + * Commonly used with [containsAtLeast] for stricter checks. + */ fun Assert.containsNone(vararg entries: String) = toEntries().containsNone(*entries) -fun Assert.containsOnly( - vararg entries: String, - includeDirs: Boolean = false, -) = toEntries().transform { actual -> - // Jar directories are represented as entries ending with a slash. - actual.filter { includeDirs || !it.endsWith('/') } -}.containsOnly(*entries) +/** + * Ensures the JAR contains only the specified entries. + * Used alone, without [containsAtLeast] or [containsNone]. + */ +fun Assert.containsOnly(vararg entries: String) = toEntries().containsOnly(*entries) private fun Assert.toEntries() = transform { actual -> actual.entries().toList().map { it.name } From 777afeddf3c70c345bedeedfb4c9610b585ced18 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Mar 2025 10:49:35 -0400 Subject: [PATCH 383/941] Check excluding stdlib entries in KotlinPluginsTest (#1371) --- .../plugins/shadow/KotlinPluginsTest.kt | 48 ++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 7ea237aac..41a92ef88 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -5,11 +5,11 @@ import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.util.JvmLang import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly 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 -import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource @@ -25,13 +25,17 @@ class KotlinPluginsTest : BasePluginTest() { projectScriptPath.writeText(projectBuildScript) } - @Test - fun compatKotlinJvmPlugin() { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun compatKotlinJvmPlugin(excludeStdlib: Boolean) { + val stdlib = compileOnlyStdlib(excludeStdlib) + projectScriptPath.writeText( """ ${getDefaultProjectBuildScript(plugin = "org.jetbrains.kotlin.jvm", withGroup = true, withVersion = true)} dependencies { implementation 'junit:junit:3.8.2' + $stdlib } """.trimIndent(), ) @@ -40,15 +44,26 @@ class KotlinPluginsTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast( + val entries = arrayOf( + "my/", + "META-INF/my.kotlin_module", mainClassEntry, *junitEntries, + *manifestEntries, ) + if (excludeStdlib) { + containsOnly(*entries) + } else { + containsAtLeast(*entries) + } } } - @Test - fun compatKmpJvmTarget() { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun compatKmpJvmTarget(excludeStdlib: Boolean) { + val stdlib = compileOnlyStdlib(excludeStdlib) + val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin) projectScriptPath.appendText( """ @@ -58,6 +73,7 @@ class KotlinPluginsTest : BasePluginTest() { commonMain { dependencies { implementation 'my:b:1.0' + $stdlib } } jvmMain { @@ -73,10 +89,18 @@ class KotlinPluginsTest : BasePluginTest() { run(shadowJarTask) assertThat(outputShadowJar).useAll { - containsAtLeast( + val entries = arrayOf( + "my/", + "META-INF/my.kotlin_module", mainClassEntry, *entriesInAB, + *manifestEntries, ) + if (excludeStdlib) { + containsOnly(*entries) + } else { + containsAtLeast(*entries) + } } } @@ -117,4 +141,14 @@ class KotlinPluginsTest : BasePluginTest() { } } } + + private fun compileOnlyStdlib(exclude: Boolean): String { + return if (exclude) { + // Disable the stdlib dependency added via `implementation`. + path("gradle.properties").writeText("kotlin.stdlib.default.dependency=false") + "compileOnly 'org.jetbrains.kotlin:kotlin-stdlib'" + } else { + "" + } + } } From c24ab5bb38ca2d005a05f62ea510740162fd7293 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Mar 2025 20:50:56 -0400 Subject: [PATCH 384/941] Document adding stdlib into compileClasspath (#1372) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- docs/kotlin-plugins/README.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index 4248ba8e4..1cba6d1ef 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -1,12 +1,32 @@ # Integrating with Kotlin Plugins -Kotlin standard libraries (stdlib) are added by Kotlin plugins by default, they will be bundled into the shadowed JARs automatically. +Kotlin standard libraries (stdlib) are added by Kotlin plugins by default via `implementation` (`runtimeClasspath`), +they will be bundled into the shadowed JARs automatically. If you don't need a standard library at all, you can add the following Gradle property to your gradle.properties file: ```properties kotlin.stdlib.default.dependency=false ``` +Kotlin compilations may still require the standard libraries, you can add them into `compileOnly` (`compileClasspath`) +to make sure compilations success and avoid shadowing as follows: + +=== "Kotlin" + + ```kotlin + dependencies { + compileOnly("org.jetbrains.kotlin:kotlin-stdlib") + } + ``` + +=== "Groovy" + + ```groovy + dependencies { + compileOnly 'org.jetbrains.kotlin:kotlin-stdlib' + } + ``` + See more information about [Dependency on the standard library](https://kotlinlang.org/docs/gradle-configure-project.html#dependency-on-the-standard-library). ## For Kotlin JVM Plugin From a804a42d9082eb91b925d220293a2ab2f1f2e68d Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Mar 2025 08:58:48 +0800 Subject: [PATCH 385/941] Rename shadowedEntries to relocatedEntries --- .../gradle/plugins/shadow/JavaPluginTest.kt | 4 ++-- .../gradle/plugins/shadow/RelocationTest.kt | 12 ++++++------ .../plugins/shadow/caching/RelocationCachingTest.kt | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index ca13399bc..e42ed45ab 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -181,7 +181,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun shadowProjectShadowJar() { writeClientAndServerModules(clientShadowed = true) - val shadowedEntries = junitEntries + val relocatedEntries = junitEntries .map { it.replace("junit/framework/", "client/junit/framework/") }.toTypedArray() run(serverShadowJarTask) @@ -193,7 +193,7 @@ class JavaPluginTest : BasePluginTest() { "client/junit/", "client/Client.class", "server/Server.class", - *shadowedEntries, + *relocatedEntries, *manifestEntries, ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 9bb353cea..eb1543a82 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -39,7 +39,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) val entryPrefix = relocationPrefix.replace('.', '/') - val shadowedEntries = buildSet { + val relocatedEntries = buildSet { addAll( junitEntries.map { "$entryPrefix/$it" } .filterNot { it.startsWith("$entryPrefix/META-INF/") }, @@ -57,7 +57,7 @@ class RelocationTest : BasePluginTest() { containsOnly( "my/", mainClassEntry, - *shadowedEntries, + *relocatedEntries, *manifestEntries, ) } @@ -168,7 +168,7 @@ class RelocationTest : BasePluginTest() { } """.trimIndent(), ) - val shadowedEntries = junitEntries + val relocatedEntries = junitEntries .map { it.replace("junit/framework/", "shadow/junit/") }.toTypedArray() path("src/main/java/my/MyTest.java").writeText( @@ -190,7 +190,7 @@ class RelocationTest : BasePluginTest() { "my/", "shadow/", "my/MyTest.class", - *shadowedEntries, + *relocatedEntries, *manifestEntries, ) } @@ -255,7 +255,7 @@ class RelocationTest : BasePluginTest() { public class App {} """.trimIndent(), ) - val shadowedEntries = junitEntries + val relocatedEntries = junitEntries .map { it.replace("junit/framework/", "app/junit/framework/") }.toTypedArray() run(":app:$SHADOW_JAR_TASK_NAME") @@ -270,7 +270,7 @@ class RelocationTest : BasePluginTest() { "app/junit/", "app/App.class", "app/core/Core.class", - *shadowedEntries, + *relocatedEntries, *manifestEntries, ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index a67ac5bca..164f7bec6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -35,7 +35,7 @@ class RelocationCachingTest : BaseCachingTest() { } """.trimIndent(), ) - val shadowedEntries = junitEntries + val relocatedEntries = junitEntries .map { it.replace("junit/framework/", "foo/junit/framework/") }.toTypedArray() assertCompositeExecutions { @@ -44,7 +44,7 @@ class RelocationCachingTest : BaseCachingTest() { "foo/", "foo/junit/", mainClassEntry, - *shadowedEntries, + *relocatedEntries, *manifestEntries, ) } From 971aec6e81d74e092da2f7ca14368d085950d977 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 26 Mar 2025 16:56:09 +0800 Subject: [PATCH 386/941] No need to disable CC for dokkaHtml --- .github/actions/deploy-site/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/deploy-site/action.yml b/.github/actions/deploy-site/action.yml index 238768994..d4d2e8a03 100644 --- a/.github/actions/deploy-site/action.yml +++ b/.github/actions/deploy-site/action.yml @@ -9,7 +9,7 @@ runs: cache-read-only: true - name: Prepare API documentation shell: bash - run: ./gradlew dokkaHtml --no-configuration-cache + run: ./gradlew dokkaHtml - name: Build Site shell: bash run: | From b1eeb71f048508034183be03037954988db75a21 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Mar 2025 05:24:02 -0400 Subject: [PATCH 387/941] Check and fix links (#1373) * Run umbrelladocs/action-linkspector for checking links https://github.com/UmbrellaDocs/action-linkspector?tab=readme-ov-file * Run Dokka first * Fix renovate-bot links * Fix more * Move the check into a new workflow file --- .github/workflows/check-links.yml | 33 +++++++++++++++++++++++++++++++ docs/changes/README.md | 22 ++++++++++----------- 2 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/check-links.yml diff --git a/.github/workflows/check-links.yml b/.github/workflows/check-links.yml new file mode 100644 index 000000000..fd3cee948 --- /dev/null +++ b/.github/workflows/check-links.yml @@ -0,0 +1,33 @@ +name: Check Links + +on: + push: + branches: + - main + paths: + - docs/** + - README.md + - RELEASING.md + pull_request: + paths: + - docs/** + - README.md + - RELEASING.md + workflow_dispatch: + +jobs: + check-links: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: true + # This is required for action-linkspector to work with API links. + - run: ./gradlew dokkaHtml + - uses: umbrelladocs/action-linkspector@v1 + with: + reporter: github-pr-check + fail_on_error: true + filter_mode: nofilter + fail_level: any diff --git a/docs/changes/README.md b/docs/changes/README.md index 5ee10af14..b31678449 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -315,15 +315,15 @@ Options: ### What's Changed * Replace deprecated ConfigureUtil by [@Goooler](https://github.com/Goooler) in [#826](https://github.com/GradleUp/shadow/pull/826) * Polish outdated configs by [@Goooler](https://github.com/Goooler) in [#831](https://github.com/GradleUp/shadow/pull/831) -* Update plugin com.gradle.enterprise to v3.12.5 by [@renovate](https://github.com/renovate) in [#838](https://github.com/GradleUp/shadow/pull/838) -* Update dependency gradle to v8.0.2 by [@renovate](https://github.com/renovate) in [#844](https://github.com/GradleUp/shadow/pull/844) -* fix(deps): update dependency org.codehaus.plexus:plexus-utils to v3.5.1 by [@renovate](https://github.com/renovate) in [#837](https://github.com/GradleUp/shadow/pull/837) -* chore(deps): update dependency prismjs to v1.27.0 [security] by [@renovate](https://github.com/renovate) in [#828](https://github.com/GradleUp/shadow/pull/828) +* Update plugin com.gradle.enterprise to v3.12.5 by [@renovate](https://github.com/renovate-bot) in [#838](https://github.com/GradleUp/shadow/pull/838) +* Update dependency gradle to v8.0.2 by [@renovate](https://github.com/renovate-bot) in [#844](https://github.com/GradleUp/shadow/pull/844) +* fix(deps): update dependency org.codehaus.plexus:plexus-utils to v3.5.1 by [@renovate](https://github.com/renovate-bot) in [#837](https://github.com/GradleUp/shadow/pull/837) +* chore(deps): update dependency prismjs to v1.27.0 [security] by [@renovate](https://github.com/renovate-bot) in [#828](https://github.com/GradleUp/shadow/pull/828) * Encode transformed properties files with specified Charset by [@scottsteen](https://github.com/scottsteen) in [#819](https://github.com/GradleUp/shadow/pull/819) -* chore(deps): update dependency vuepress to v1.9.9 by [@renovate](https://github.com/renovate) in [#842](https://github.com/GradleUp/shadow/pull/842) +* chore(deps): update dependency vuepress to v1.9.9 by [@renovate](https://github.com/renovate-bot) in [#842](https://github.com/GradleUp/shadow/pull/842) ### New Contributors -* [@renovate](https://github.com/renovate) made their first contribution in [#838](https://github.com/GradleUp/shadow/pull/838) +* [@renovate](https://github.com/renovate-bot) made their first contribution in [#838](https://github.com/GradleUp/shadow/pull/838) * [@scottsteen](https://github.com/scottsteen) made their first contribution in [#819](https://github.com/GradleUp/shadow/pull/819) **Full Changelog**: [`8.1.0...8.1.1`](https://github.com/GradleUp/shadow/compare/8.1.0...8.1.1) @@ -520,7 +520,7 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Thu, 27 Mar 2025 10:23:39 +0800 Subject: [PATCH 391/941] Eliminate unused warning --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index fd34eea09..b0fd11981 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -127,7 +127,7 @@ testing.suites { // This part should be placed after testing.suites to ensure the test sourceSets are created. kotlin.target.compilations { val main by getting - val functionalTest by getting { + getByName("functionalTest") { // TODO: https://youtrack.jetbrains.com/issue/KTIJ-7662 associateWith(main) } From e28b527a6f8280a916816e5cc00ae240f1e6251d Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 27 Mar 2025 10:27:13 +0800 Subject: [PATCH 392/941] Replace `publish` with `publishToMavenCentral` --- .github/workflows/ci.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 185449ca6..76fe640b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: with: cache-read-only: true # TODO: https://github.com/gradle/gradle/issues/22779 - - run: ./gradlew publish --no-configuration-cache + - run: ./gradlew publishToMavenCentral --no-configuration-cache env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0aded3585..e80f084f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -25,7 +25,7 @@ jobs: with: cache-read-only: true # TODO: https://github.com/gradle/gradle/issues/22779 - - run: ./gradlew publish publishPlugins --no-configuration-cache + - run: ./gradlew publishToMavenCentral publishPlugins --no-configuration-cache env: GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_KEY }} GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_SECRET }} From 078d567472ec58faa6c582f3a0bdaf40b4c3b0c2 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 27 Mar 2025 10:32:13 +0800 Subject: [PATCH 393/941] Divide properties --- gradle.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gradle.properties b/gradle.properties index 962bd3557..cdee2034f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g org.gradle.kotlin.dsl.allWarningsAsErrors=true org.gradle.parallel=true +########## Properties for publishing to Maven Central ########## + GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin VERSION_NAME=9.0.0-SNAPSHOT From 17d497fb2e7f42cd8bd7bfa9d63eb60f698e9f9a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Mar 2025 22:55:16 -0400 Subject: [PATCH 394/941] More const values for configurations (#1374) Run `diff -r shadow-before/ shadow-after > diff.txt`: ```diff diff --color -r shadow-before/com.gradleup.shadow.gradle.plugin/9.0.0-SNAPSHOT/maven-metadata-local.xml shadow-after/com.gradleup.shadow.gradle.plugin/9.0.0-SNAPSHOT/maven-metadata-local.xml 6c6 < 20250327025234 --- > 20250327025303 12c12 < pom.asc --- > pom 14c14 < 20250327025234 --- > 20250327025303 17c17 < pom --- > pom.asc 19c19 < 20250327025234 --- > 20250327025303 diff --color -r shadow-before/com.gradleup.shadow.gradle.plugin/maven-metadata-local.xml shadow-after/com.gradleup.shadow.gradle.plugin/maven-metadata-local.xml 10c10 < 20250327025234 --- > 20250327025303 diff --color -r shadow-before/shadow-gradle-plugin/9.0.0-SNAPSHOT/maven-metadata-local.xml shadow-after/shadow-gradle-plugin/9.0.0-SNAPSHOT/maven-metadata-local.xml 6c6 < 20250327025241 --- > 20250327025307 12,13c12 < sources < jar.asc --- > pom 15c14 < 20250327025241 --- > 20250327025307 18c17,18 < module.asc --- > sources > jar.asc 20c20 < 20250327025241 --- > 20250327025307 23,24c23 < javadoc < jar.asc --- > jar 26c25 < 20250327025241 --- > 20250327025307 29c28 < javadoc --- > sources 32c31 < 20250327025241 --- > 20250327025307 35c34 < jar.asc --- > module 37c36 < 20250327025241 --- > 20250327025307 40c39 < module --- > jar.asc 42c41 < 20250327025241 --- > 20250327025307 44a44 > javadoc 47c47 < 20250327025241 --- > 20250327025307 50c50 < pom.asc --- > module.asc 52c52 < 20250327025241 --- > 20250327025307 55c55 < pom --- > pom.asc 57c57 < 20250327025241 --- > 20250327025307 60,61c60,61 < sources < jar --- > javadoc > jar.asc 63c63 < 20250327025241 --- > 20250327025307 diff --color -r shadow-before/shadow-gradle-plugin/maven-metadata-local.xml shadow-after/shadow-gradle-plugin/maven-metadata-local.xml 10c10 < 20250327025241 --- > 20250327025307 ``` --- .../shadow.convention.publish.gradle.kts | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts index 0f17fc466..812b24d35 100644 --- a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts @@ -1,3 +1,8 @@ +import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME + plugins { id("com.gradle.plugin-publish") id("com.vanniktech.maven.publish") @@ -40,19 +45,18 @@ tasks.publishPlugins { } } -configurations { - listOf( - apiElements, - runtimeElements, - named("javadocElements"), - named("sourcesElements"), - ).forEach { - it.configure { +configurations.configureEach { + when (name) { + API_ELEMENTS_CONFIGURATION_NAME, + RUNTIME_ELEMENTS_CONFIGURATION_NAME, + JAVADOC_ELEMENTS_CONFIGURATION_NAME, + SOURCES_ELEMENTS_CONFIGURATION_NAME, + -> { outgoing { - // Main/current capability + // Main/current capability. capability("com.gradleup.shadow:shadow-gradle-plugin:$version") - // Historical capabilities + // Historical capabilities. capability("io.github.goooler.shadow:shadow-gradle-plugin:$version") capability("com.github.johnrengelman:shadow:$version") capability("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:$version") @@ -65,8 +69,8 @@ configurations { publishing.publications.withType().configureEach { // We don't care about capabilities being unmappable to Maven - suppressPomMetadataWarningsFor("apiElements") - suppressPomMetadataWarningsFor("runtimeElements") - suppressPomMetadataWarningsFor("javadocElements") - suppressPomMetadataWarningsFor("sourcesElements") + suppressPomMetadataWarningsFor(API_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(RUNTIME_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(JAVADOC_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(SOURCES_ELEMENTS_CONFIGURATION_NAME) } From 120a5aafdbc8d9c0c26a0d52a59f0baea53d67be Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Mar 2025 23:08:04 -0400 Subject: [PATCH 395/941] No need to silence the compatibility warnings (#1375) Run `diff -r shadow-before/ shadow-after > diff.txt`: ```diff diff --color -r shadow-before/com.gradleup.shadow.gradle.plugin/9.0.0-SNAPSHOT/maven-metadata-local.xml shadow-after/com.gradleup.shadow.gradle.plugin/9.0.0-SNAPSHOT/maven-metadata-local.xml 6c6 < 20250327025833 --- > 20250327030008 14c14 < 20250327025833 --- > 20250327030008 19c19 < 20250327025833 --- > 20250327030008 diff --color -r shadow-before/com.gradleup.shadow.gradle.plugin/maven-metadata-local.xml shadow-after/com.gradleup.shadow.gradle.plugin/maven-metadata-local.xml 10c10 < 20250327025833 --- > 20250327030008 diff --color -r shadow-before/shadow-gradle-plugin/9.0.0-SNAPSHOT/maven-metadata-local.xml shadow-after/shadow-gradle-plugin/9.0.0-SNAPSHOT/maven-metadata-local.xml 6c6 < 20250327025833 --- > 20250327030008 11a12,16 > pom.asc > 9.0.0-SNAPSHOT > 20250327030008 > > 15c20 < 20250327025833 --- > 20250327030008 18c23,24 < module --- > sources > jar.asc 20c26 < 20250327025833 --- > 20250327030008 23,24c29,30 < javadoc < jar.asc --- > sources > jar 26c32 < 20250327025833 --- > 20250327030008 31c37 < 20250327025833 --- > 20250327030008 34c40 < jar.asc --- > module 36c42 < 20250327025833 --- > 20250327030008 39d44 < sources 42c47 < 20250327025833 --- > 20250327030008 47c52 < 20250327025833 --- > 20250327030008 50,51c55,56 < sources < jar --- > javadoc > jar.asc 53c58 < 20250327025833 --- > 20250327030008 58,63c63 < 20250327025833 < < < pom.asc < 9.0.0-SNAPSHOT < 20250327025833 --- > 20250327030008 diff --color -r shadow-before/shadow-gradle-plugin/maven-metadata-local.xml shadow-after/shadow-gradle-plugin/maven-metadata-local.xml 10c10 < 20250327025833 --- > 20250327030008 ``` --- .../src/main/kotlin/shadow.convention.publish.gradle.kts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts index 812b24d35..b9229c73b 100644 --- a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts @@ -66,11 +66,3 @@ configurations.configureEach { } } } - -publishing.publications.withType().configureEach { - // We don't care about capabilities being unmappable to Maven - suppressPomMetadataWarningsFor(API_ELEMENTS_CONFIGURATION_NAME) - suppressPomMetadataWarningsFor(RUNTIME_ELEMENTS_CONFIGURATION_NAME) - suppressPomMetadataWarningsFor(JAVADOC_ELEMENTS_CONFIGURATION_NAME) - suppressPomMetadataWarningsFor(SOURCES_ELEMENTS_CONFIGURATION_NAME) -} From 7d204ca3f32ebc9696fb5abf56b1e7f634774af3 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 27 Mar 2025 11:14:42 +0800 Subject: [PATCH 396/941] Revert "No need to silence the compatibility warnings (#1375)" This reverts commit 120a5aafdbc8d9c0c26a0d52a59f0baea53d67be. --- .../src/main/kotlin/shadow.convention.publish.gradle.kts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts index b9229c73b..34a9a9349 100644 --- a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts @@ -66,3 +66,11 @@ configurations.configureEach { } } } + +publishing.publications.withType().configureEach { + // We don't care about capabilities being unmappable to Maven. + suppressPomMetadataWarningsFor(API_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(RUNTIME_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(JAVADOC_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(SOURCES_ELEMENTS_CONFIGURATION_NAME) +} From dccc02d61ce56acb68f9712f460e3569f0db6239 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 29 Mar 2025 23:05:21 -0400 Subject: [PATCH 397/941] Back to ffurrer2/extract-release-notes@v2 (#1381) https://github.com/ffurrer2/extract-release-notes/releases/tag/v2.3.0 --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e80f084f4..9b77555ac 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -35,7 +35,7 @@ jobs: ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} - uses: ./.github/actions/deploy-site - name: Extract release notes - uses: ffurrer2/extract-release-notes@main + uses: ffurrer2/extract-release-notes@v2 with: changelog_file: docs/changes/README.md release_notes_file: RELEASE_NOTES.md From b0097e96fa5615980182fbdb5625f6e36fbc9edc Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 31 Mar 2025 22:45:42 -0400 Subject: [PATCH 398/941] Replace composite deployment with the general workflow (#1382) * Replace composite deployment with the normal one * Remove limits for testing * Move ./.github/workflows/deploy.yml usage into a new job * Add workflow_call * Add maven-central env for release job * Revert "Remove limits for testing" This reverts commit ac8b5090 --- .github/actions/deploy-site/action.yml | 22 ---------------------- .github/workflows/deploy.yml | 17 +++++++++++++++-- .github/workflows/release.yml | 9 +++++---- 3 files changed, 20 insertions(+), 28 deletions(-) delete mode 100644 .github/actions/deploy-site/action.yml diff --git a/.github/actions/deploy-site/action.yml b/.github/actions/deploy-site/action.yml deleted file mode 100644 index d4d2e8a03..000000000 --- a/.github/actions/deploy-site/action.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: 'Deploy Site' -description: 'Deploy site to GitHub Pages' - -runs: - using: 'composite' - steps: - - uses: gradle/actions/setup-gradle@v4 - with: - cache-read-only: true - - name: Prepare API documentation - shell: bash - run: ./gradlew dokkaHtml - - name: Build Site - shell: bash - run: | - # Don't cache it to track updates. - pip install mkdocs-material - mkdocs build - - uses: actions/upload-pages-artifact@v3 - with: - path: site - - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1be19d144..a07bac0ec 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,6 +1,7 @@ name: Deploy on: + workflow_call: workflow_dispatch: jobs: @@ -10,9 +11,21 @@ jobs: environment: name: github-pages permissions: - contents: write id-token: write pages: write steps: - uses: actions/checkout@v4 - - uses: ./.github/actions/deploy-site + - uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: true + - name: Prepare API documentation + run: ./gradlew dokkaHtml + - name: Build Site + run: | + # Don't cache it to track updates. + pip install mkdocs-material + mkdocs build + - uses: actions/upload-pages-artifact@v3 + with: + path: site + - uses: actions/deploy-pages@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9b77555ac..245de4902 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,11 +10,9 @@ jobs: runs-on: ubuntu-latest if: github.event.repository.fork == false environment: - name: github-pages + name: maven-central permissions: contents: write - id-token: write - pages: write steps: - uses: actions/checkout@v4 - uses: actions/setup-java@v4 @@ -33,7 +31,6 @@ jobs: ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} - - uses: ./.github/actions/deploy-site - name: Extract release notes uses: ffurrer2/extract-release-notes@v2 with: @@ -43,3 +40,7 @@ jobs: run: gh release create ${{ github.ref_name }} --notes-file RELEASE_NOTES.md env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + deploy: + needs: release + uses: ./.github/workflows/deploy.yml From ede8437a2b13d060aec43f626a56774d2e0fd843 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 1 Apr 2025 12:10:39 +0800 Subject: [PATCH 399/941] Prepare version 9.0.0-beta12 --- docs/changes/README.md | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index b31678449..99f8174bc 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,8 +1,8 @@ # Change Log -## [Unreleased] -[Unreleased]: https://github.com/GradleUp/shadow/compare/9.0.0-beta11...HEAD +## [v9.0.0-beta12] (2025-04-01) +[v9.0.0-beta12]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12 **Added** diff --git a/gradle.properties b/gradle.properties index cdee2034f..cfb8eaad1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta12 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 09a2b4701c8b3a9a89cb5d45302bde6b88d98864 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 1 Apr 2025 12:14:03 +0800 Subject: [PATCH 400/941] Prepare next development version --- docs/changes/README.md | 4 ++++ gradle.properties | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 99f8174bc..af01c77f0 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,10 @@ # Change Log +## [Unreleased] +[Unreleased]: https://github.com/GradleUp/shadow/compare/9.0.0-beta12...HEAD + + ## [v9.0.0-beta12] (2025-04-01) [v9.0.0-beta12]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12 diff --git a/gradle.properties b/gradle.properties index cfb8eaad1..cdee2034f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ org.gradle.parallel=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta12 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=DEFAULT From 5d6910eceaaeaf42e756517d74579899dd05ab6b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 1 Apr 2025 09:31:35 -0400 Subject: [PATCH 401/941] Enable Mkdocs validation (#1383) * Warn unrecognized_links and anchors * Run mkdocs build --strict https://www.mkdocs.org/user-guide/configuration/#validation * Trigger the workflow by current file changes * Install mkdocs only * Revert "Install mkdocs only" This reverts commit 57d1c717659ca3557b2fa11c6463523b15da961f. --- .github/workflows/links.yml | 7 +++++++ mkdocs.yml | 4 ++++ 2 files changed, 11 insertions(+) diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index f99ad298d..467eddf25 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -8,11 +8,13 @@ on: - docs/** - README.md - RELEASING.md + - .github/workflows/links.yml pull_request: paths: - docs/** - README.md - RELEASING.md + - .github/workflows/links.yml workflow_dispatch: jobs: @@ -25,6 +27,11 @@ jobs: cache-read-only: true # This is required for action-linkspector to work with API links. - run: ./gradlew dokkaHtml + - name: Mkdocs build validation + run: | + # Don't cache it to track updates. + pip install mkdocs-material + mkdocs build --strict - uses: umbrelladocs/action-linkspector@v1 with: reporter: github-pr-check diff --git a/mkdocs.yml b/mkdocs.yml index 35e8c29e9..7b80bb20d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -77,3 +77,7 @@ nav: - 'Changes': changes/README.md - 'API': api/index.html - 'About': about/README.md + +validation: + unrecognized_links: warn + anchors: warn From c6e6aa991d4331a2c6b44618a004acc487a1dd90 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 2 Apr 2025 05:02:04 -0400 Subject: [PATCH 402/941] Unify Kotlin plugins in docs (#1384) --- docs/kotlin-plugins/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index 1cba6d1ef..92f027ed6 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -37,7 +37,7 @@ Shadow works well for Kotlin JVM projects like Java projects. Here is an example ```kotlin plugins { - kotlin("jvm") + id("org.jetbrains.kotlin.jvm") id("com.gradleup.shadow") } @@ -73,7 +73,7 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. ```kotlin plugins { - kotlin("multiplatform") + id("org.jetbrains.kotlin.multiplatform") id("com.gradleup.shadow") } From 96453a108c8eefab79e36799a03aeb9eba894f5c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 6 Apr 2025 09:31:45 +0800 Subject: [PATCH 403/941] Update dependency org.codehaus.plexus:plexus-xml to v4.1.0 (#1385) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 502368ee8..b645c03fb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,7 +12,7 @@ asm = "org.ow2.asm:asm-commons:9.7.1" jdependency = "org.vafer:jdependency:2.12" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" -plexus-xml = "org.codehaus.plexus:plexus-xml:4.0.4" +plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" 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" } From 6dfa26eef048ff2b5bf95c3d191f959ca8611e7d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 7 Apr 2025 09:27:09 -0400 Subject: [PATCH 404/941] Avoid creating jvm targets eagerly for KMP (#1378) * Test `doNotCreateJvmTargetEagerly` * Defer KotlinJvmTarget configuration * Update changelog * Suppress `OPT_IN_USAGE` * Try `tasks.named("shadowJar")` in Kotlin example * Test `compatKmpForOtherNamedJvmTarget` * Obtain info from named jvm target --- docs/changes/README.md | 4 ++ docs/kotlin-plugins/README.md | 4 +- .../plugins/shadow/KotlinPluginsTest.kt | 67 +++++++++++++++++++ .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 27 +++++--- 4 files changed, 90 insertions(+), 12 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index af01c77f0..ccde7b443 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -4,6 +4,10 @@ ## [Unreleased] [Unreleased]: https://github.com/GradleUp/shadow/compare/9.0.0-beta12...HEAD +**Fixed** + +- Avoid creating jvm targets eagerly for KMP. ([#1378](https://github.com/GradleUp/shadow/pull/1378)) + ## [v9.0.0-beta12] (2025-04-01) [v9.0.0-beta12]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12 diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index 92f027ed6..753b5cb25 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -80,6 +80,7 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. val ktorVersion = "3.1.0" kotlin { + @Suppress("OPT_IN_USAGE") jvm().mainRun { // Optionally, set the main class for `runJvm`, it's available from Kotlin 2.1.0 mainClass = "myapp.MainKt" @@ -98,7 +99,8 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. } } - tasks.shadowJar { + // TODO: we can't use `tasks.shadowJar` here like the other examples, something wrong with Gradle or Kotlin plugin? + tasks.named("shadowJar") { manifest { // Optionally, set the main class for the shadowed JAR. attributes["Main-Class"] = "com.example.MainKt" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 41a92ef88..9ae2e0b0d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -1,8 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat +import assertk.assertions.contains import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey +import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JvmLang import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsOnly @@ -10,6 +12,7 @@ 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 +import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource @@ -104,6 +107,70 @@ class KotlinPluginsTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/1377", + ) + @Test + fun compatKmpForOtherNamedJvmTarget() { + val jvmTargetName = "newJvm" + val jvmTargetMain = "${jvmTargetName}Main" + val stdlib = compileOnlyStdlib(true) + val mainClassEntry = writeClass(sourceSet = jvmTargetMain, jvmLang = JvmLang.Kotlin) + projectScriptPath.appendText( + """ + kotlin { + jvm("$jvmTargetName") + sourceSets { + commonMain { + dependencies { + implementation 'my:b:1.0' + $stdlib + } + } + $jvmTargetMain { + dependencies { + implementation 'my:a:1.0' + } + } + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + val entries = arrayOf( + "my/", + "META-INF/my.kotlin_module", + mainClassEntry, + *entriesInAB, + *manifestEntries, + ) + containsAtLeast(*entries) + } + } + + @Issue( + "https://github.com/GradleUp/shadow/issues/1377", + ) + @Test + fun doNotCreateJvmTargetEagerly() { + projectScriptPath.appendText( + """ + kotlin { + mingwX64() + } + """.trimIndent(), + ) + + val result = runWithFailure(shadowJarTask) + + assertThat(result.output).contains( + "Cannot locate tasks that match ':shadowJar' as task 'shadowJar' not found in root project", + ) + } + @ParameterizedTest @ValueSource(booleans = [false, true]) fun canSetMainClassAttribute(useShadowAttr: Boolean) { 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 b2204a1fe..6d7666192 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 @@ -9,6 +9,7 @@ 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 +import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget public abstract class ShadowKmpPlugin : Plugin { private lateinit var kmpExtension: KotlinMultiplatformExtension @@ -16,24 +17,28 @@ public abstract class ShadowKmpPlugin : Plugin { override fun apply(project: Project) { with(project) { kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java) - val kotlinJvmMain = kmpExtension.jvm().compilations.named("main") - registerShadowJarCommon { task -> - task.from(kotlinJvmMain.map { it.output.allOutputs }) - task.configurations.convention( - provider { - listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) - }, - ) - configureMainClass(task) + kmpExtension.targets.configureEach { target -> + if (target !is KotlinJvmTarget) return@configureEach + + val kotlinJvmMain = target.compilations.named("main") + registerShadowJarCommon { task -> + task.from(kotlinJvmMain.map { it.output.allOutputs }) + task.configurations.convention( + provider { + listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) + }, + ) + configureMainClass(target.name, task) + } } } } - private fun Project.configureMainClass(task: ShadowJar) { + private fun Project.configureMainClass(targetName: String, task: ShadowJar) { if (KgpVersion.DEFAULT < KgpVersion.KOTLIN_2_1) return @OptIn(ExperimentalKotlinGradlePluginApi::class) - kmpExtension.jvm().mainRun { + kmpExtension.jvm(targetName).mainRun { // Fix cannot serialize object of type 'org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmRun'. val mainClassName = provider { mainClass } task.inputs.property("mainClassName", mainClassName) From 51ced57126bc7af4bf76666f726441506e132fae Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 7 Apr 2025 10:18:37 -0400 Subject: [PATCH 405/941] Rearrange ShadowJar logic in ShadowKmpPlugin (#1386) --- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 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 6d7666192..a876bf149 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 @@ -2,7 +2,6 @@ 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 @@ -12,40 +11,39 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as KgpVersion import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget public abstract class ShadowKmpPlugin : Plugin { - private lateinit var kmpExtension: KotlinMultiplatformExtension override fun apply(project: Project) { with(project) { - kmpExtension = extensions.getByType(KotlinMultiplatformExtension::class.java) - kmpExtension.targets.configureEach { target -> + extensions.getByType(KotlinMultiplatformExtension::class.java).targets.configureEach { target -> if (target !is KotlinJvmTarget) return@configureEach - val kotlinJvmMain = target.compilations.named("main") - registerShadowJarCommon { task -> - task.from(kotlinJvmMain.map { it.output.allOutputs }) - task.configurations.convention( - provider { - listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) - }, - ) - configureMainClass(target.name, task) - } + configureShadowJar(target) } } } - private fun Project.configureMainClass(targetName: String, task: ShadowJar) { - if (KgpVersion.DEFAULT < KgpVersion.KOTLIN_2_1) return + private fun Project.configureShadowJar(target: KotlinJvmTarget) { + val kotlinJvmMain = target.compilations.named("main") + registerShadowJarCommon { task -> + task.from(kotlinJvmMain.map { it.output.allOutputs }) + task.configurations.convention( + provider { + listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) + }, + ) + + if (KgpVersion.DEFAULT < KgpVersion.KOTLIN_2_1) return@registerShadowJarCommon - @OptIn(ExperimentalKotlinGradlePluginApi::class) - kmpExtension.jvm(targetName).mainRun { - // 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 { - val realClass = mainClassName.get().orNull - if (!task.manifest.attributes.contains(mainClassAttributeKey) && !realClass.isNullOrEmpty()) { - task.manifest.attributes[mainClassAttributeKey] = realClass + @OptIn(ExperimentalKotlinGradlePluginApi::class) + target.mainRun { + // 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 { + val realClass = mainClassName.get().orNull + if (!task.manifest.attributes.contains(mainClassAttributeKey) && !realClass.isNullOrEmpty()) { + task.manifest.attributes[mainClassAttributeKey] = realClass + } } } } From 243ff2874770ad5543c77d78479f75ff84d9ec2e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 03:30:18 +0000 Subject: [PATCH 406/941] Update plugin spotless to v7.0.3 (#1387) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b645c03fb..0bd5bcc36 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,4 +34,4 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.9.1" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" -spotless = "com.diffplug.spotless:7.0.2" +spotless = "com.diffplug.spotless:7.0.3" From d9bf97f2b7a2c184876abf3214d1cc3d4bd5febb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 8 Apr 2025 17:11:45 +0800 Subject: [PATCH 407/941] Delete .gemini directory --- .gemini/config.yaml | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 .gemini/config.yaml diff --git a/.gemini/config.yaml b/.gemini/config.yaml deleted file mode 100644 index fc1f1d49b..000000000 --- a/.gemini/config.yaml +++ /dev/null @@ -1,9 +0,0 @@ -have_fun: true -code_review: - disable: false - comment_severity_threshold: MEDIUM - max_review_comments: -1 - pull_request_opened: - help: false - summary: false - code_review: true From e62fd0088d7bdc1b6d7851288e9e18142423dd98 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 9 Apr 2025 09:15:25 +0800 Subject: [PATCH 408/941] Update plugin com.gradle.develocity to v4 (#1388) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 53e2b4cfd..6bfdd411b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "3.19.2" + id("com.gradle.develocity") version "4.0" } develocity { From bfc9b297c46e3e6e7fc6bb41c383a5830f825a75 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 10 Apr 2025 10:55:43 +0800 Subject: [PATCH 409/941] CRLF for gradlew.bat --- gradlew.bat | 188 ++++++++++++++++++++++++++-------------------------- 1 file changed, 94 insertions(+), 94 deletions(-) diff --git a/gradlew.bat b/gradlew.bat index 9b42019c7..9d21a2183 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,94 +1,94 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=. -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 8a2dfa3227e5b4fd3283cf5603b412c5ba5f2268 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 10 Apr 2025 03:10:05 -0400 Subject: [PATCH 410/941] Update jdependency 2.11 version link (#1391) --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ccde7b443..612739b71 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -275,7 +275,7 @@ Options: **Changed** - `ShadowExtension.component` has been deprecated, now you can use `component.shadow` instead. ([#956](https://github.com/GradleUp/shadow/pull/956)) -- **BREAKING CHANGE:** update to [jdependency 2.11.0](https://github.com/tcurdt/jdependency/blob/master/HISTORY.md#version-2110-release-18112024), this requires Java 11 or above to run. ([#974](https://github.com/GradleUp/shadow/pull/974)) +- **BREAKING CHANGE:** update to [jdependency 2.11](https://github.com/tcurdt/jdependency/releases/tag/jdependency-2.11), this requires Java 11 or above to run. ([#974](https://github.com/GradleUp/shadow/pull/974)) **Fixed** From efab13bc5d5fed2a044cad5267dcc61b66cc6c16 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 10 Apr 2025 03:23:13 -0400 Subject: [PATCH 411/941] Update Jar.from references (#1390) Prefer ```java AbstractCopyTask from(Object sourcePath, Action configureAction) ``` over ```java AbstractCopyTask from(Object sourcePath, Closure c) ``` --- docs/configuration/README.md | 2 +- docs/configuration/dependencies/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index daa4c82da..f4322bc59 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -152,7 +152,7 @@ on the `shadowJar.manifest` object can be used to configure the upstream. ## Adding Extra Files The `shadowJar` task is a subclass of the `Jar` task, which means that the -[from](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20groovy.lang.Closure)) +[Jar.from](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action)) method can be used to add extra files. === "Kotlin" diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index eab1e43b9..5b11295f6 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -32,7 +32,7 @@ have the intended effect, as `configurations.compile` will try to delegate to th ## Embedding Jar Files Inside Your Shadow Jar The `shadowJar` task is a subclass of the `Jar` task, which means that the -[from](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20groovy.lang.Closure)) +[Jar.from](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action)) method can be used to add extra files. === "Kotlin" From 3dbc100241037c79eb56c22990f751c3930fea10 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 10 Apr 2025 07:32:23 +0000 Subject: [PATCH 412/941] Update dependency org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin to v0.10.0 (#1392) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0bd5bcc36..e25d996ee 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,7 +20,7 @@ moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "mosh pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.1" mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:0.31.0" jetbrains-dokka = "org.jetbrains.dokka:dokka-gradle-plugin:2.0.0" -foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:0.9.0" +foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:0.10.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" From 08d84459e9765944a6bd0a820f5bf57b4ceb77d0 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 10 Apr 2025 04:59:25 -0400 Subject: [PATCH 413/941] Honor keep-a-changelog header formats (#1394) https://keepachangelog.com/en/1.1.0/ --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 197 ++++++++++++++++++++++++----------------- 1 file changed, 114 insertions(+), 83 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 612739b71..4a6c6d10c 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -9,8 +9,8 @@ - Avoid creating jvm targets eagerly for KMP. ([#1378](https://github.com/GradleUp/shadow/pull/1378)) -## [v9.0.0-beta12] (2025-04-01) -[v9.0.0-beta12]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12 +## [9.0.0-beta12] - 2025-04-01 +[9.0.0-beta12]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12 **Added** @@ -31,8 +31,8 @@ Options: - Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) -## [v9.0.0-beta11] (2025-03-18) -[v9.0.0-beta11]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta11 +## [9.0.0-beta11] - 2025-03-18 +[9.0.0-beta11]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta11 **Added** @@ -58,8 +58,8 @@ Options: - **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) -## [v9.0.0-beta10] (2025-03-05) -[v9.0.0-beta10]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta10 +## [9.0.0-beta10] - 2025-03-05 +[9.0.0-beta10]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta10 **Added** @@ -82,8 +82,8 @@ Options: - **BREAKING CHANGE:** Remove `Named` from the parents of `Transformer`. ([#1289](https://github.com/GradleUp/shadow/pull/1289)) -## [v9.0.0-beta9] (2025-02-24) -[v9.0.0-beta9]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta9 +## [9.0.0-beta9] - 2025-02-24 +[9.0.0-beta9]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta9 **Added** @@ -108,8 +108,8 @@ Options: - **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -## [v9.0.0-beta8] (2025-02-08) -[v9.0.0-beta8]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8 +## [9.0.0-beta8] - 2025-02-08 +[9.0.0-beta8]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8 **Added** @@ -127,8 +127,8 @@ Options: - **BREAKING CHANGE:** Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) -## [v9.0.0-beta7] (2025-02-02) -[v9.0.0-beta7]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta7 +## [9.0.0-beta7] - 2025-02-02 +[9.0.0-beta7]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta7 **Added** @@ -153,8 +153,8 @@ Options: - **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) -## [v9.0.0-beta6] (2025-01-23) -[v9.0.0-beta6]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta6 +## [9.0.0-beta6] - 2025-01-23 +[9.0.0-beta6]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta6 **Added** @@ -165,8 +165,8 @@ Options: - Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) -## [v9.0.0-beta5] (2025-01-21) -[v9.0.0-beta5]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta5 +## [9.0.0-beta5] - 2025-01-21 +[9.0.0-beta5]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta5 **Added** @@ -186,8 +186,8 @@ Options: - Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) -## [v9.0.0-beta4] (2024-12-06) -[v9.0.0-beta4]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta4 +## [9.0.0-beta4] - 2024-12-06 +[9.0.0-beta4]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta4 **Changed** @@ -199,8 +199,8 @@ Options: This fixes the regression for registering custom `ShadowJar` tasks. -## [v9.0.0-beta2] (2024-11-28) -[v9.0.0-beta2]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta2 +## [9.0.0-beta2] - 2024-11-28 +[9.0.0-beta2]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta2 **Fixed** @@ -208,8 +208,8 @@ Options: This fixes the relocation not working in `v9.0.0-beta1`. -## [v9.0.0-beta1] (2024-11-27) -[v9.0.0-beta1]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta1 +## [9.0.0-beta1] - 2024-11-27 +[9.0.0-beta1]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta1 **Added** @@ -233,16 +233,16 @@ Options: - Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) -## [v8.3.6] (2025-02-02) -[v8.3.6]: https://github.com/GradleUp/shadow/releases/tag/8.3.6 +## [8.3.6] - 2025-02-02 +[8.3.6]: https://github.com/GradleUp/shadow/releases/tag/8.3.6 **Added** - Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) -## [v8.3.5] (2024-11-03) -[v8.3.5]: https://github.com/GradleUp/shadow/releases/tag/8.3.5 +## [8.3.5] - 2024-11-03 +[8.3.5]: https://github.com/GradleUp/shadow/releases/tag/8.3.5 **Fixed** @@ -250,23 +250,23 @@ Options: This reverts the change to maintain compatibility with 8.x versions. The Java level will be bumped to 11 or above in the next major release. -## [v8.3.4] (2024-10-29) -[v8.3.4]: https://github.com/GradleUp/shadow/releases/tag/8.3.4 +## [8.3.4] - 2024-10-29 +[8.3.4]: https://github.com/GradleUp/shadow/releases/tag/8.3.4 **Fixed** - Apply legacy plugin last, and declare capabilities for old plugins, fixes [#964](https://github.com/GradleUp/shadow/issues/964). ([#991](https://github.com/GradleUp/shadow/pull/991)) -## [v8.3.3] (2024-10-02) -[v8.3.3]: https://github.com/GradleUp/shadow/releases/tag/8.3.3 +## [8.3.3] - 2024-10-02 +[8.3.3]: https://github.com/GradleUp/shadow/releases/tag/8.3.3 **Changed** - Disable Develocity integration by default. ([#993](https://github.com/GradleUp/shadow/pull/993)) -## [v8.3.2] (2024-09-18) -[v8.3.2]: https://github.com/GradleUp/shadow/releases/tag/8.3.2 +## [8.3.2] - 2024-09-18 +[8.3.2]: https://github.com/GradleUp/shadow/releases/tag/8.3.2 **Added** @@ -282,8 +282,8 @@ Options: - Stop publishing Shadow self fat jar to Maven repository. ([#967](https://github.com/GradleUp/shadow/pull/967)) -## [v8.3.1] (2024-09-10) -[v8.3.1]: https://github.com/GradleUp/shadow/releases/tag/8.3.1 +## [8.3.1] - 2024-09-10 +[8.3.1]: https://github.com/GradleUp/shadow/releases/tag/8.3.1 **Added** @@ -296,8 +296,8 @@ Options: - Refix excluding Gradle APIs for java-gradle-plugin. ([#948](https://github.com/GradleUp/shadow/pull/948)) -## [v8.3.0] (2024-08-08) -[v8.3.0]: https://github.com/GradleUp/shadow/releases/tag/8.3.0 +## [8.3.0] - 2024-08-08 +[8.3.0]: https://github.com/GradleUp/shadow/releases/tag/8.3.0 **Changed** @@ -315,8 +315,8 @@ Options: `8.1.1`. ([#858](https://github.com/GradleUp/shadow/pull/858)) -## [v8.1.1] (2023-03-20) -[v8.1.1]: https://github.com/GradleUp/shadow/releases/tag/8.1.1 +## [8.1.1] - 2023-03-20 +[8.1.1]: https://github.com/GradleUp/shadow/releases/tag/8.1.1 **NOTE:** As of this version, the GitHub repository has migrated to the `main` branch as the default branch for releases. @@ -337,8 +337,8 @@ Options: **Full Changelog**: [`8.1.0...8.1.1`](https://github.com/GradleUp/shadow/compare/8.1.0...8.1.1) -## [v8.1.0] (2023-02-26) -[v8.1.0]: https://github.com/GradleUp/shadow/releases/tag/8.1.0 +## [8.1.0] - 2023-02-26 +[8.1.0]: https://github.com/GradleUp/shadow/releases/tag/8.1.0 **BREAKING CHANGE:** Due to adoption of the latest version of the `com.gradle.plugin-publish` plugin, the maven GAV coordinates have changed as of this version. The correct coordinates now align with the plugin ID itself: `group=com.github.johnrengelman, artifact=shadow, version=`. @@ -358,8 +358,8 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Thu, 10 Apr 2025 07:26:34 -0400 Subject: [PATCH 414/941] Document publishing with custom archive properties (#1389) * Update `publishShadowJarWithCustomClassifierAndExtension` * Update `applyPlugin` * Update `Configuring Output Name` * Note `Publish the Shadowed JAR with Custom Artifact Name` * Refine --- docs/configuration/README.md | 35 ++++++--- docs/configuration/merging/README.md | 2 +- docs/publishing/README.md | 73 +++++++++++++++++++ .../gradle/plugins/shadow/JavaPluginTest.kt | 15 ++-- .../gradle/plugins/shadow/PublishingTest.kt | 37 +++++++++- 5 files changed, 142 insertions(+), 20 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index f4322bc59..21fa1b080 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -13,21 +13,35 @@ details. ## Configuring Output Name -Shadow configures the default `shadowJar` task to set the output JAR's `destinationDirectory`, `archiveBaseName`, `appendix`, -`archiveVersion`, and `extension` to the same default values as Gradle does for all `Jar` tasks. -Additionally, it configures the `archiveClassifier` to be `all`. +Shadow configures the default `shadowJar` task to set the output JAR's + +- [`archiveAppendix`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveAppendix) +- [`archiveBaseName`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveBaseName) +- [`archiveExtension`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:archiveExtension) +- [`archiveFile`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:archiveFile) +- [`archiveFileName`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:archiveFileName) +- [`archiveVersion`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:archiveVersion) +- [`destinationDirectory`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:destinationDirectory) + +to the same default values as Gradle does for all `Jar` tasks. +Additionally, it configures the +[`archiveClassifier`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveClassifier) +to be `all`. The listed ones are not full, you can view all the properties in +[`Jar`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html). +The output shadowed JAR file will be named with the following format: -If working with a Gradle project with the name `myApp` and archiveVersion `1.0`, the default `shadowJar` task will output a -file at: `build/libs/myApp-1.0-all.jar` +``` +archiveBaseName-$archiveAppendix-$archiveVersion-$archiveClassifier.$archiveExtension +``` -As with all `Jar` tasks in Gradle, these values can be overridden: +If working with a Gradle project with the name `myApp` and version `1.0`, the default `shadowJar` task will output a +file at: `build/libs/myApp-1.0-all.jar`. You can override the properties listed above to change the output name of the +shadowed JAR file. e.g. === "Kotlin" ```kotlin tasks.shadowJar { - archiveBaseName = "shadow" - archiveClassifier = "" archiveVersion = "" } ``` @@ -36,12 +50,13 @@ As with all `Jar` tasks in Gradle, these values can be overridden: ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - archiveBaseName = 'shadow' - archiveClassifier = '' archiveVersion = '' } ``` +This will result in the output file being named `myApp-all.jar` instead of `myApp-1.0-all.jar`. + + ## Configuring the Runtime Classpath Each Java JAR file contains a manifest file that provides metadata about the contents of the JAR file itself. diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 5f5360f82..d5c821944 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -368,7 +368,7 @@ which means it honors the `duplicatesStrategy` property as its parent classes do - `INHERIT`: Uses the same strategy as the parent copy specification. - `WARN`: Do not attempt to prevent duplicates, but log a warning message when multiple items are to be created at the same path. -You can see more details about them in +see more details about them in [`DuplicatesStrategy`](https://docs.gradle.org/current/javadoc/org/gradle/api/file/DuplicatesStrategy.html). `ShadowJar` recognizes `DuplicatesStrategy.INCLUDE` as the default, if you want to change the strategy, you can diff --git a/docs/publishing/README.md b/docs/publishing/README.md index e888b53f4..534c8b229 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -218,3 +218,76 @@ It is possible to publish a custom `ShadowJar` task's output via the [`MavenPubl } } ``` + + +## Publish the Shadowed JAR with Custom Artifact Name + +It is possible to configure the artifact name of the shadowed JAR via properties like `archiveBaseName`, see more +customizable properties listed in [Configuring Output Name](../configuration/README.md#configuring-output-name). e.g. + +=== "Kotlin" + + ```kotlin + plugins { + java + `maven-publish` + id("com.gradleup.shadow") + } + + group = "my-group" + version = "1.0" + + tasks.shadowJar { + archiveClassifier = "my-classifier" + archiveExtension = "my-ext" + archiveBaseName = "maven-all" + } + + publishing { + publications { + create("shadow") { + from(components["shadow"]) + // This will override `archiveBaseName`. + artifactId = "my-artifact" + } + } + repositories { + maven("https://repo.myorg.com") + } + } + ``` + +=== "Groovy" + + ```groovy + plugins { + id 'java' + id 'maven-publish' + id 'com.gradleup.shadow' + } + + group = 'my-group' + version = '1.0' + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + archiveClassifier = 'my-classifier' + archiveExtension = 'my-ext' + archiveBaseName = 'maven-all' + } + + publishing { + publications { + shadow(MavenPublication) { + from components.shadow + // This will override `archiveBaseName`. + artifactId = 'my-artifact' + } + } + repositories { + maven { url = 'https://repo.myorg.com' } + } + } + ``` + +We modified `archiveClassifier`, `archiveExtension` and `archiveBaseName` in this example, the published artifact will +be named `my-artifact-2.0-my-classifier.my-ext` instead of `1.0-all.jar`. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index e42ed45ab..0d9960b3f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -34,6 +34,7 @@ import kotlin.io.path.name import kotlin.io.path.outputStream import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.tasks.bundling.Jar import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledForJreRange @@ -60,20 +61,24 @@ class JavaPluginTest : BasePluginTest() { val shadowTask = project.tasks.getByName(SHADOW_JAR_TASK_NAME) as ShadowJar val shadowConfig = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) - with(shadowTask) { + // Check extended properties. + with(shadowTask as Jar) { + assertThat(archiveAppendix.orNull).isNull() assertThat(archiveBaseName.get()).isEqualTo(projectName) - assertThat(destinationDirectory.get().asFile) - .isEqualTo(project.layout.buildDirectory.dir("libs").get().asFile) - assertThat(archiveVersion.get()).isEqualTo(version) assertThat(archiveClassifier.get()).isEqualTo("all") assertThat(archiveExtension.get()).isEqualTo("jar") assertThat(archiveFileName.get()).isEqualTo("my-shadow-1.0.0-all.jar") + assertThat(archiveVersion.get()).isEqualTo(version) assertThat(archiveFile.get().asFile).all { isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) isEqualTo(project.projectDir.resolve("build/libs/my-shadow-1.0.0-all.jar")) } - assertThat(archiveAppendix.orNull).isNull() + assertThat(destinationDirectory.get().asFile) + .isEqualTo(project.layout.buildDirectory.dir("libs").get().asFile) + } + // Check self properties. + with(shadowTask) { assertThat(minimizeJar.get()).isFalse() assertThat(enableRelocation.get()).isFalse() assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 2c907d66b..fdcf0f0ee 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -214,26 +214,55 @@ class PublishingTest : BasePluginTest() { } @Issue( + "https://github.com/GradleUp/shadow/issues/614", "https://github.com/GradleUp/shadow/issues/860", "https://github.com/GradleUp/shadow/issues/945", ) @Test - fun publishShadowJarWithCustomClassifierAndExtension() { + fun publishShadowJarWithCustomArtifactName() { projectScriptPath.appendText( publishConfiguration( + projectBlock = """ + group = 'my-group' + version = '2.0' + """.trimIndent(), shadowBlock = """ archiveClassifier = 'my-classifier' archiveExtension = 'my-ext' archiveBaseName = 'maven-all' """.trimIndent(), + publicationsBlock = """ + shadow(MavenPublication) { + from components.shadow + artifactId = 'my-artifact' + } + """.trimIndent(), ), ) publish() - assertShadowJarCommon(repoJarPath("my/maven-all/1.0/maven-all-1.0-my-classifier.my-ext")) - assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module"))) + assertThat(repoPath("my-group/my-artifact/2.0/").listDirectoryEntries().map { it.name }).containsOnly( + "my-artifact-2.0-my-classifier.my-ext.sha512", + "my-artifact-2.0-my-classifier.my-ext", + "my-artifact-2.0.pom.sha256", + "my-artifact-2.0.module", + "my-artifact-2.0.pom", + "my-artifact-2.0.module.sha256", + "my-artifact-2.0.module.sha1", + "my-artifact-2.0.module.md5", + "my-artifact-2.0.pom.sha512", + "my-artifact-2.0-my-classifier.my-ext.sha256", + "my-artifact-2.0.module.sha512", + "my-artifact-2.0-my-classifier.my-ext.sha1", + "my-artifact-2.0-my-classifier.my-ext.md5", + "my-artifact-2.0.pom.md5", + "my-artifact-2.0.pom.sha1", + ) + + assertShadowJarCommon(repoJarPath("my-group/my-artifact/2.0/my-artifact-2.0-my-classifier.my-ext")) + assertPomCommon(repoPath("my-group/my-artifact/2.0/my-artifact-2.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my-group/my-artifact/2.0/my-artifact-2.0.module"))) } @Test From 5e20cb536fa187dc3873d6856ae60236ef7ca25a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:22:54 +0000 Subject: [PATCH 415/941] Update dependency org.junit:junit-bom to v5.12.2 (#1395) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e25d996ee..6bfd09335 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,7 +27,7 @@ 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. ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" -junit-bom = "org.junit:junit-bom:5.12.1" +junit-bom = "org.junit:junit-bom:5.12.2" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From 24da3092e0ac1fa10d8c2a66e24f4f865624cacd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 12 Apr 2025 12:50:26 +0800 Subject: [PATCH 416/941] Update dependency commons-io:commons-io to v2.19.0 (#1396) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6bfd09335..600f9b239 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,7 +4,7 @@ moshi = "1.15.2" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" -apache-commonsIo = "commons-io:commons-io:2.18.0" +apache-commonsIo = "commons-io:commons-io:2.19.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.9" asm = "org.ow2.asm:asm-commons:9.7.1" From 965efe68468e42ee12083fc2812b47619743da15 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 13 Apr 2025 02:24:19 -0400 Subject: [PATCH 417/941] Use dokka 2 (#1397) https://kotlinlang.org/docs/dokka-migration.html --- .github/workflows/deploy.yml | 2 +- .github/workflows/links.yml | 2 +- .../main/kotlin/shadow.convention.publish.gradle.kts | 10 ++++++---- build.gradle.kts | 2 +- gradle.properties | 5 +++++ 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index a07bac0ec..3cb23261e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -19,7 +19,7 @@ jobs: with: cache-read-only: true - name: Prepare API documentation - run: ./gradlew dokkaHtml + run: ./gradlew dokkaGenerate - name: Build Site run: | # Don't cache it to track updates. diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index 467eddf25..5a5c6c5c6 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -26,7 +26,7 @@ jobs: with: cache-read-only: true # This is required for action-linkspector to work with API links. - - run: ./gradlew dokkaHtml + - run: ./gradlew dokkaGenerate - name: Mkdocs build validation run: | # Don't cache it to track updates. diff --git a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts index 34a9a9349..67c647837 100644 --- a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts +++ b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts @@ -13,6 +13,12 @@ version = providers.gradleProperty("VERSION_NAME").get() group = providers.gradleProperty("GROUP").get() description = providers.gradleProperty("POM_DESCRIPTION").get() +dokka { + dokkaPublications.html { + outputDirectory = rootDir.resolve("docs/api") + } +} + java { withSourcesJar() withJavadocJar() @@ -33,10 +39,6 @@ gradlePlugin { } } -tasks.dokkaHtml { - outputDirectory = rootDir.resolve("docs/api") -} - tasks.publishPlugins { doFirst { if (version.toString().endsWith("SNAPSHOT")) { diff --git a/build.gradle.kts b/build.gradle.kts index b0fd11981..63c7f4f64 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -181,7 +181,7 @@ tasks.clean { delete += listOf( rootDirs.map { it.resolve(".gradle") }, rootDirs.map { it.resolve(".kotlin") }, - tasks.dokkaHtml.map { it.outputDirectory }, + dokka.dokkaPublications.html.map { it.outputDirectory }, // Generated by MkDocs. rootDir.resolve("site"), ) diff --git a/gradle.properties b/gradle.properties index cdee2034f..54f32c831 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,11 @@ org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g org.gradle.kotlin.dsl.allWarningsAsErrors=true org.gradle.parallel=true +org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled +org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true +org.jetbrains.dokka.experimental.tryK2=true +org.jetbrains.dokka.experimental.tryK2.nowarn=true + ########## Properties for publishing to Maven Central ########## GROUP=com.gradleup.shadow From f3d6dad5f78d7c1dec47ac78cd79616db9eab173 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 13 Apr 2025 16:10:21 -0400 Subject: [PATCH 418/941] Eliminate convention plugin (#1398) --- build-logic/build.gradle.kts | 9 -- build-logic/gradle.properties | 1 - build-logic/settings.gradle.kts | 12 --- .../shadow.convention.publish.gradle.kts | 78 ----------------- build.gradle.kts | 87 +++++++++++++++---- gradle/libs.versions.toml | 8 +- settings.gradle.kts | 2 - 7 files changed, 75 insertions(+), 122 deletions(-) delete mode 100644 build-logic/build.gradle.kts delete mode 100644 build-logic/gradle.properties delete mode 100644 build-logic/settings.gradle.kts delete mode 100644 build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts deleted file mode 100644 index 84ff20fd0..000000000 --- a/build-logic/build.gradle.kts +++ /dev/null @@ -1,9 +0,0 @@ -plugins { - `kotlin-dsl` -} - -dependencies { - implementation(libs.pluginPublish) - implementation(libs.mavenPublish) - implementation(libs.jetbrains.dokka) -} diff --git a/build-logic/gradle.properties b/build-logic/gradle.properties deleted file mode 100644 index 338c1c3fc..000000000 --- a/build-logic/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -org.gradle.kotlin.dsl.allWarningsAsErrors=true diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts deleted file mode 100644 index 8f0b5dc0b..000000000 --- a/build-logic/settings.gradle.kts +++ /dev/null @@ -1,12 +0,0 @@ -dependencyResolutionManagement { - versionCatalogs { - create("libs") { - from(files("../gradle/libs.versions.toml")) - } - } - - repositories { - mavenCentral() - gradlePluginPortal() - } -} diff --git a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts b/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts deleted file mode 100644 index 67c647837..000000000 --- a/build-logic/src/main/kotlin/shadow.convention.publish.gradle.kts +++ /dev/null @@ -1,78 +0,0 @@ -import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME -import org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME -import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME -import org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME - -plugins { - id("com.gradle.plugin-publish") - id("com.vanniktech.maven.publish") - id("org.jetbrains.dokka") -} - -version = providers.gradleProperty("VERSION_NAME").get() -group = providers.gradleProperty("GROUP").get() -description = providers.gradleProperty("POM_DESCRIPTION").get() - -dokka { - dokkaPublications.html { - outputDirectory = rootDir.resolve("docs/api") - } -} - -java { - withSourcesJar() - withJavadocJar() -} - -gradlePlugin { - website = providers.gradleProperty("POM_URL") - vcsUrl = providers.gradleProperty("POM_URL") - - plugins { - create("shadowPlugin") { - id = "com.gradleup.shadow" - implementationClass = "com.github.jengelman.gradle.plugins.shadow.ShadowPlugin" - displayName = providers.gradleProperty("POM_NAME").get() - description = providers.gradleProperty("POM_DESCRIPTION").get() - tags = listOf("onejar", "shade", "fatjar", "uberjar") - } - } -} - -tasks.publishPlugins { - doFirst { - if (version.toString().endsWith("SNAPSHOT")) { - error("Cannot publish SNAPSHOT versions to Plugin Portal!") - } - } -} - -configurations.configureEach { - when (name) { - API_ELEMENTS_CONFIGURATION_NAME, - RUNTIME_ELEMENTS_CONFIGURATION_NAME, - JAVADOC_ELEMENTS_CONFIGURATION_NAME, - SOURCES_ELEMENTS_CONFIGURATION_NAME, - -> { - outgoing { - // Main/current capability. - capability("com.gradleup.shadow:shadow-gradle-plugin:$version") - - // Historical capabilities. - capability("io.github.goooler.shadow:shadow-gradle-plugin:$version") - capability("com.github.johnrengelman:shadow:$version") - capability("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:$version") - capability("gradle.plugin.com.github.johnrengelman:shadow:$version") - capability("com.github.jengelman.gradle.plugins:shadow:$version") - } - } - } -} - -publishing.publications.withType().configureEach { - // We don't care about capabilities being unmappable to Maven. - suppressPomMetadataWarningsFor(API_ELEMENTS_CONFIGURATION_NAME) - suppressPomMetadataWarningsFor(RUNTIME_ELEMENTS_CONFIGURATION_NAME) - suppressPomMetadataWarningsFor(JAVADOC_ELEMENTS_CONFIGURATION_NAME) - suppressPomMetadataWarningsFor(SOURCES_ELEMENTS_CONFIGURATION_NAME) -} diff --git a/build.gradle.kts b/build.gradle.kts index 63c7f4f64..b4c3f8e2e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,9 @@ @file:Suppress("UnstableApiUsage") +import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion @@ -7,8 +11,20 @@ plugins { alias(libs.plugins.kotlin.jvm) alias(libs.plugins.android.lint) alias(libs.plugins.jetbrains.bcv) + alias(libs.plugins.jetbrains.dokka) + alias(libs.plugins.mavenPublish) + alias(libs.plugins.pluginPublish) alias(libs.plugins.spotless) - id("shadow.convention.publish") +} + +version = providers.gradleProperty("VERSION_NAME").get() +group = providers.gradleProperty("GROUP").get() +description = providers.gradleProperty("POM_DESCRIPTION").get() + +dokka { + dokkaPublications.html { + outputDirectory = rootDir.resolve("docs/api") + } } java { @@ -40,8 +56,6 @@ spotless { } kotlinGradle { ktlint(libs.ktlint.get().version) - target("**/*.kts") - targetExclude("build-logic/build/**") } } @@ -49,6 +63,36 @@ val testPluginClasspath by configurations.registering { isCanBeResolved = true } +configurations.configureEach { + when (name) { + API_ELEMENTS_CONFIGURATION_NAME, + RUNTIME_ELEMENTS_CONFIGURATION_NAME, + JAVADOC_ELEMENTS_CONFIGURATION_NAME, + SOURCES_ELEMENTS_CONFIGURATION_NAME, + -> { + outgoing { + // Main/current capability. + capability("com.gradleup.shadow:shadow-gradle-plugin:$version") + + // Historical capabilities. + capability("io.github.goooler.shadow:shadow-gradle-plugin:$version") + capability("com.github.johnrengelman:shadow:$version") + capability("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:$version") + capability("gradle.plugin.com.github.johnrengelman:shadow:$version") + capability("com.github.jengelman.gradle.plugins:shadow:$version") + } + } + } +} + +publishing.publications.withType().configureEach { + // We don't care about capabilities being unmappable to Maven. + suppressPomMetadataWarningsFor(API_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(RUNTIME_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(JAVADOC_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(SOURCES_ELEMENTS_CONFIGURATION_NAME) +} + dependencies { compileOnly(libs.kotlin.kmp) implementation(libs.apache.ant) @@ -61,8 +105,8 @@ dependencies { implementation(libs.plexus.xml) testPluginClasspath(libs.foojayResolver) - testPluginClasspath(libs.pluginPublish) testPluginClasspath(libs.kotlin.kmp) + testPluginClasspath(libs.pluginPublish) lintChecks(libs.androidx.gradlePluginLints) } @@ -124,6 +168,26 @@ testing.suites { } } +gradlePlugin { + website = providers.gradleProperty("POM_URL") + vcsUrl = providers.gradleProperty("POM_URL") + + plugins { + create("shadowPlugin") { + id = "com.gradleup.shadow" + implementationClass = "com.github.jengelman.gradle.plugins.shadow.ShadowPlugin" + displayName = providers.gradleProperty("POM_NAME").get() + description = providers.gradleProperty("POM_DESCRIPTION").get() + tags = listOf("onejar", "shade", "fatjar", "uberjar") + } + } + + testSourceSets( + sourceSets["functionalTest"], + sourceSets["documentTest"], + ) +} + // This part should be placed after testing.suites to ensure the test sourceSets are created. kotlin.target.compilations { val main by getting @@ -133,13 +197,6 @@ kotlin.target.compilations { } } -gradlePlugin { - testSourceSets( - sourceSets["functionalTest"], - sourceSets["documentTest"], - ) -} - tasks.pluginUnderTestMetadata { // Plugins used in tests could be resolved in classpath. pluginClasspath.from( @@ -174,13 +231,9 @@ tasks.register("downloadStartScripts") { } tasks.clean { - val includedBuilds = gradle.includedBuilds - dependsOn(includedBuilds.map { it.task(path) }) - - val rootDirs = includedBuilds.map { it.projectDir } + projectDir delete += listOf( - rootDirs.map { it.resolve(".gradle") }, - rootDirs.map { it.resolve(".kotlin") }, + projectDir.resolve(".gradle"), + projectDir.resolve(".kotlin"), dokka.dokkaPublications.html.map { it.outputDirectory }, // Generated by MkDocs. rootDir.resolve("site"), diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 600f9b239..42c44e027 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] kotlin = "2.1.20" moshi = "1.15.2" +pluginPublish = "1.3.1" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" @@ -17,11 +18,9 @@ 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" } -pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.1" -mavenPublish = "com.vanniktech:gradle-maven-publish-plugin:0.31.0" -jetbrains-dokka = "org.jetbrains.dokka:dokka-gradle-plugin:2.0.0" foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:0.10.0" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } +pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } 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. @@ -34,4 +33,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.9.1" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" +jetbrains-dokka = "org.jetbrains.dokka:2.0.0" +mavenPublish = "com.vanniktech.maven.publish:0.31.0" +pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } spotless = "com.diffplug.spotless:7.0.3" diff --git a/settings.gradle.kts b/settings.gradle.kts index 6bfdd411b..2cfa55c0e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,8 +10,6 @@ pluginManagement { } gradlePluginPortal() } - - includeBuild("build-logic") } plugins { From 6135ff4a8f241b70a65257cb9cfd2d810e2dffb3 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 14 Apr 2025 04:14:14 +0800 Subject: [PATCH 419/941] Remove pom metadata warning suppressions These warnings may occur on publishing to MavenCentral, but this step happens on CI. We have no necessary to care about it. --- build.gradle.kts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index b4c3f8e2e..ecf3e636b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -85,14 +85,6 @@ configurations.configureEach { } } -publishing.publications.withType().configureEach { - // We don't care about capabilities being unmappable to Maven. - suppressPomMetadataWarningsFor(API_ELEMENTS_CONFIGURATION_NAME) - suppressPomMetadataWarningsFor(RUNTIME_ELEMENTS_CONFIGURATION_NAME) - suppressPomMetadataWarningsFor(JAVADOC_ELEMENTS_CONFIGURATION_NAME) - suppressPomMetadataWarningsFor(SOURCES_ELEMENTS_CONFIGURATION_NAME) -} - dependencies { compileOnly(libs.kotlin.kmp) implementation(libs.apache.ant) From e817284ca1e95df6f525a1bc652f2ef68f553c52 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 14 Apr 2025 04:29:53 +0800 Subject: [PATCH 420/941] Revert "Remove pom metadata warning suppressions" I'm wrong, the warnings also show on publishing to MavenLocal. This reverts commit 6135ff4a8f241b70a65257cb9cfd2d810e2dffb3. --- build.gradle.kts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index ecf3e636b..b4c3f8e2e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -85,6 +85,14 @@ configurations.configureEach { } } +publishing.publications.withType().configureEach { + // We don't care about capabilities being unmappable to Maven. + suppressPomMetadataWarningsFor(API_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(RUNTIME_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(JAVADOC_ELEMENTS_CONFIGURATION_NAME) + suppressPomMetadataWarningsFor(SOURCES_ELEMENTS_CONFIGURATION_NAME) +} + dependencies { compileOnly(libs.kotlin.kmp) implementation(libs.apache.ant) From 27b18f9c3006c6d30510f94f711616a73e125f78 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 15 Apr 2025 04:57:28 -0400 Subject: [PATCH 421/941] Inline header links in changelog (#1399) This should make the release steps easier. --- docs/changes/README.md | 175 ++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 116 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 4a6c6d10c..528075881 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,16 +1,14 @@ # Change Log -## [Unreleased] -[Unreleased]: https://github.com/GradleUp/shadow/compare/9.0.0-beta12...HEAD +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta12...HEAD) - 2025-xx-xx **Fixed** - Avoid creating jvm targets eagerly for KMP. ([#1378](https://github.com/GradleUp/shadow/pull/1378)) -## [9.0.0-beta12] - 2025-04-01 -[9.0.0-beta12]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12 +## [9.0.0-beta12](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12) - 2025-04-01 **Added** @@ -31,8 +29,7 @@ Options: - Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) -## [9.0.0-beta11] - 2025-03-18 -[9.0.0-beta11]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta11 +## [9.0.0-beta11](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta11) - 2025-03-18 **Added** @@ -58,8 +55,7 @@ Options: - **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) -## [9.0.0-beta10] - 2025-03-05 -[9.0.0-beta10]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta10 +## [9.0.0-beta10](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta10) - 2025-03-05 **Added** @@ -82,8 +78,7 @@ Options: - **BREAKING CHANGE:** Remove `Named` from the parents of `Transformer`. ([#1289](https://github.com/GradleUp/shadow/pull/1289)) -## [9.0.0-beta9] - 2025-02-24 -[9.0.0-beta9]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta9 +## [9.0.0-beta9](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta9) - 2025-02-24 **Added** @@ -108,8 +103,7 @@ Options: - **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -## [9.0.0-beta8] - 2025-02-08 -[9.0.0-beta8]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8 +## [9.0.0-beta8](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8) - 2025-02-08 **Added** @@ -127,8 +121,7 @@ Options: - **BREAKING CHANGE:** Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) -## [9.0.0-beta7] - 2025-02-02 -[9.0.0-beta7]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta7 +## [9.0.0-beta7](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta7) - 2025-02-02 **Added** @@ -153,8 +146,7 @@ Options: - **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) -## [9.0.0-beta6] - 2025-01-23 -[9.0.0-beta6]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta6 +## [9.0.0-beta6](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta6) - 2025-01-23 **Added** @@ -165,8 +157,7 @@ Options: - Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) -## [9.0.0-beta5] - 2025-01-21 -[9.0.0-beta5]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta5 +## [9.0.0-beta5](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta5) - 2025-01-21 **Added** @@ -186,8 +177,7 @@ Options: - Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) -## [9.0.0-beta4] - 2024-12-06 -[9.0.0-beta4]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta4 +## [9.0.0-beta4](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta4) - 2024-12-06 **Changed** @@ -199,8 +189,7 @@ Options: This fixes the regression for registering custom `ShadowJar` tasks. -## [9.0.0-beta2] - 2024-11-28 -[9.0.0-beta2]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta2 +## [9.0.0-beta2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta2) - 2024-11-28 **Fixed** @@ -208,8 +197,7 @@ Options: This fixes the relocation not working in `v9.0.0-beta1`. -## [9.0.0-beta1] - 2024-11-27 -[9.0.0-beta1]: https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta1 +## [9.0.0-beta1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta1) - 2024-11-27 **Added** @@ -233,16 +221,14 @@ Options: - Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) -## [8.3.6] - 2025-02-02 -[8.3.6]: https://github.com/GradleUp/shadow/releases/tag/8.3.6 +## [8.3.6](https://github.com/GradleUp/shadow/releases/tag/8.3.6) - 2025-02-02 **Added** - Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) -## [8.3.5] - 2024-11-03 -[8.3.5]: https://github.com/GradleUp/shadow/releases/tag/8.3.5 +## [8.3.5](https://github.com/GradleUp/shadow/releases/tag/8.3.5) - 2024-11-03 **Fixed** @@ -250,23 +236,20 @@ Options: This reverts the change to maintain compatibility with 8.x versions. The Java level will be bumped to 11 or above in the next major release. -## [8.3.4] - 2024-10-29 -[8.3.4]: https://github.com/GradleUp/shadow/releases/tag/8.3.4 +## [8.3.4](https://github.com/GradleUp/shadow/releases/tag/8.3.4) - 2024-10-29 **Fixed** - Apply legacy plugin last, and declare capabilities for old plugins, fixes [#964](https://github.com/GradleUp/shadow/issues/964). ([#991](https://github.com/GradleUp/shadow/pull/991)) -## [8.3.3] - 2024-10-02 -[8.3.3]: https://github.com/GradleUp/shadow/releases/tag/8.3.3 +## [8.3.3](https://github.com/GradleUp/shadow/releases/tag/8.3.3) - 2024-10-02 **Changed** - Disable Develocity integration by default. ([#993](https://github.com/GradleUp/shadow/pull/993)) -## [8.3.2] - 2024-09-18 -[8.3.2]: https://github.com/GradleUp/shadow/releases/tag/8.3.2 +## [8.3.2](https://github.com/GradleUp/shadow/releases/tag/8.3.2) - 2024-09-18 **Added** @@ -282,8 +265,7 @@ Options: - Stop publishing Shadow self fat jar to Maven repository. ([#967](https://github.com/GradleUp/shadow/pull/967)) -## [8.3.1] - 2024-09-10 -[8.3.1]: https://github.com/GradleUp/shadow/releases/tag/8.3.1 +## [8.3.1](https://github.com/GradleUp/shadow/releases/tag/8.3.1) - 2024-09-10 **Added** @@ -296,8 +278,7 @@ Options: - Refix excluding Gradle APIs for java-gradle-plugin. ([#948](https://github.com/GradleUp/shadow/pull/948)) -## [8.3.0] - 2024-08-08 -[8.3.0]: https://github.com/GradleUp/shadow/releases/tag/8.3.0 +## [8.3.0](https://github.com/GradleUp/shadow/releases/tag/8.3.0) - 2024-08-08 **Changed** @@ -315,8 +296,7 @@ Options: `8.1.1`. ([#858](https://github.com/GradleUp/shadow/pull/858)) -## [8.1.1] - 2023-03-20 -[8.1.1]: https://github.com/GradleUp/shadow/releases/tag/8.1.1 +## [8.1.1](https://github.com/GradleUp/shadow/releases/tag/8.1.1) - 2023-03-20 **NOTE:** As of this version, the GitHub repository has migrated to the `main` branch as the default branch for releases. @@ -337,8 +317,7 @@ Options: **Full Changelog**: [`8.1.0...8.1.1`](https://github.com/GradleUp/shadow/compare/8.1.0...8.1.1) -## [8.1.0] - 2023-02-26 -[8.1.0]: https://github.com/GradleUp/shadow/releases/tag/8.1.0 +## [8.1.0](https://github.com/GradleUp/shadow/releases/tag/8.1.0) - 2023-02-26 **BREAKING CHANGE:** Due to adoption of the latest version of the `com.gradle.plugin-publish` plugin, the maven GAV coordinates have changed as of this version. The correct coordinates now align with the plugin ID itself: `group=com.github.johnrengelman, artifact=shadow, version=`. @@ -358,8 +337,7 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Date: Tue, 15 Apr 2025 17:06:01 +0800 Subject: [PATCH 422/941] Remove top block in docs/configuration/README.md --- docs/configuration/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 21fa1b080..2580a2899 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -1,7 +1,3 @@ ---- -api: api/com/github/jengelman/gradle/plugins/shadow ---- - # Configuring Shadow The [`ShadowJar`](../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type extends from Gradle's From 3c6d5063a3927a574ca5ff7ab5bdf8b051514552 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 15 Apr 2025 23:48:08 -0400 Subject: [PATCH 423/941] Use more footnotes in docs (#1400) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/README.md | 7 +- docs/application-plugin/README.md | 31 ++++--- docs/configuration/README.md | 71 ++++++++------- docs/configuration/dependencies/README.md | 35 +++++--- docs/configuration/filtering/README.md | 19 ++-- docs/configuration/merging/README.md | 101 ++++++++++++---------- docs/custom-tasks/README.md | 14 ++- docs/getting-started/README.md | 30 ++++--- docs/gradle-plugins/README.md | 15 ++-- docs/kotlin-plugins/README.md | 9 +- docs/multi-project/README.md | 7 +- docs/publishing/README.md | 12 ++- 12 files changed, 209 insertions(+), 142 deletions(-) diff --git a/docs/README.md b/docs/README.md index 15f729b08..311649150 100644 --- a/docs/README.md +++ b/docs/README.md @@ -8,7 +8,7 @@ Shadow is a Gradle plugin for combining a project's dependency classes and resources into a single output Jar. The combined Jar is often referred to a _fat-jar_ or _uber-jar_. -Shadow utilizes [`JarInputStream`](https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarInputStream.html) and [`JarOutputStream`](https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarOutputStream.html) to efficiently process dependent libraries +Shadow utilizes [`JarInputStream`][JarInputStream] and [`JarOutputStream`][JarOutputStream] to efficiently process dependent libraries into the output jar without incurring the I/O overhead of expanding the jars to disk. !!! warning "Plugin ID Change" @@ -57,3 +57,8 @@ These issues often manifest themselves as binary incompatibilities in either the By utilizing Shadow's ability to _relocate_ the package names for dependencies, a library author can ensure that the library's dependencies will not conflict with the same dependency being declared by the downstream application. + + + +[JarInputStream]: https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarInputStream.html +[JarOutputStream]: https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarOutputStream.html diff --git a/docs/application-plugin/README.md b/docs/application-plugin/README.md index 09a33b116..1058fd132 100644 --- a/docs/application-plugin/README.md +++ b/docs/application-plugin/README.md @@ -1,11 +1,11 @@ # Integrating with Application Plugin -Shadow reacts to the presence of Gradle's -[`application`](https://docs.gradle.org/current/userguide/application_plugin.html) plugin and will automatically -configure additional tasks for running the shadowed JAR and creating distributions containing the shadowed JAR. +Shadow reacts to the presence of Gradle's [`application`][application] plugin and will automatically configure +additional tasks for running the shadowed JAR and creating distributions containing the shadowed JAR. -Just like the normal `jar` task, when the `application` plugin is applied, the `shadowJar` manifest will be -configured to contain the `Main-Class` attribute with the value specified in the project's `mainClassName` attribute. +Just like the normal [`Jar`][Jar] task, when the [`application`][application] plugin is applied, +the [`ShadowJar`][ShadowJar] manifest will be configured to contain the `Main-Class` attribute with the value specified +in the project's `application.mainClass` attribute. === "Kotlin" @@ -37,9 +37,8 @@ configured to contain the `Main-Class` attribute with the value specified in the When applied along with the `application` plugin, the `runShadow` task will be created for starting the application from the shadowed JAR. -The `runShadow` task is a [`JavaExec`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html) -task that is configured to execute `java -jar myproject-all.jar`. -It can be configured the same as any other `JavaExec` task. +The `runShadow` task is a [`JavaExec`][JavaExec] task that is configured to execute `java -jar myproject-all.jar`. +It can be configured the same as any other [`JavaExec`][JavaExec] task. === "Kotlin" @@ -81,10 +80,9 @@ It can be configured the same as any other `JavaExec` task. ## Distributing the Shadow JAR -The Shadow plugin will also configure distribution tasks when in the presence of the `application` plugin. -The plugin will create `shadowDistZip` and `shadowDistTar` which creates Zip and Tar distributions -respectively. -Each distribution will contain the shadowed JAR file along with the necessary start scripts to launch +The Shadow plugin will also configure distribution tasks when in the presence of the [`application`][application] +plugin. The plugin will create `shadowDistZip` and `shadowDistTar` which creates Zip and Tar distributions +respectively. Each distribution will contain the shadowed JAR file along with the necessary start scripts to launch the application. Additionally, the plugin will create the `installShadowDist` and `startShadowScripts` tasks which stages the necessary @@ -142,5 +140,12 @@ You can also add more files into the distribution like: } ``` -View [the official doc described](https://docs.gradle.org/current/userguide/distribution_plugin.html#distribution_plugin) +View [The Distribution Plugin](https://docs.gradle.org/current/userguide/distribution_plugin.html#distribution_plugin) for more information about configuring distributions. + + + +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[JavaExec]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.JavaExec.html +[ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html +[application]: https://docs.gradle.org/current/userguide/application_plugin.html diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 2580a2899..f1767ea33 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -1,38 +1,32 @@ # Configuring Shadow -The [`ShadowJar`](../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type extends from Gradle's -[`Jar`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) type. -This means that all attributes and methods available on `Jar` are also available on -[`ShadowJar`](../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html). -Refer the _Gradle User Guide_ for [Jar](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) for -details. +The [`ShadowJar`][ShadowJar] task type extends from Gradle's [`Jar`][Jar] type. +This means that all attributes and methods available on [`Jar`][Jar] are also available on [`ShadowJar`][ShadowJar]. + ## Configuring Output Name -Shadow configures the default `shadowJar` task to set the output JAR's +Shadow configures the default [`ShadowJar`][ShadowJar] task to set the output JAR's -- [`archiveAppendix`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveAppendix) -- [`archiveBaseName`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveBaseName) -- [`archiveExtension`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:archiveExtension) -- [`archiveFile`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:archiveFile) -- [`archiveFileName`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:archiveFileName) -- [`archiveVersion`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:archiveVersion) -- [`destinationDirectory`](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:destinationDirectory) +- [`archiveAppendix`][archiveAppendix] +- [`archiveBaseName`][archiveBaseName] +- [`archiveExtension`][archiveExtension] +- [`archiveFile`][archiveFile] +- [`archiveFileName`][archiveFileName] +- [`archiveVersion`][archiveVersion] +- [`destinationDirectory`][destinationDirectory] -to the same default values as Gradle does for all `Jar` tasks. -Additionally, it configures the -[`archiveClassifier`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveClassifier) -to be `all`. The listed ones are not full, you can view all the properties in -[`Jar`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html). -The output shadowed JAR file will be named with the following format: +to the same default values as Gradle does for all [`Jar`][Jar] tasks. Additionally, it configures the +[`archiveClassifier`][archiveClassifier] to be `all`. The listed ones are not full, you can view all the properties in +[`Jar`][Jar]. The output shadowed JAR file will be named with the following format: ``` archiveBaseName-$archiveAppendix-$archiveVersion-$archiveClassifier.$archiveExtension ``` -If working with a Gradle project with the name `myApp` and version `1.0`, the default `shadowJar` task will output a -file at: `build/libs/myApp-1.0-all.jar`. You can override the properties listed above to change the output name of the -shadowed JAR file. e.g. +If working with a Gradle project with the name `myApp` and version `1.0`, the default [`ShadowJar`][ShadowJar] task will +output a file at: `build/libs/myApp-1.0-all.jar`. You can override the properties listed above to change the output name +of the shadowed JAR file. e.g. === "Kotlin" @@ -64,10 +58,10 @@ they are still required for runtime execution. In these scenarios, Shadow creates a `shadow` configuration to declare these dependencies. Dependencies added to the `shadow` configuration are **not** bundled into the output JAR. Think of `configurations.shadow` as unmerged, runtime dependencies. -The integration with the `maven-publish` plugin will automatically configure dependencies added +The integration with the [`maven-publish`][maven-publish] plugin will automatically configure dependencies added to `configurations.shadow` as `RUNTIME` scope dependencies in the resulting POM file. -Additionally, Shadow automatically configures the manifest of the `shadowJar` task to contain a `Class-Path` entry +Additionally, Shadow automatically configures the manifest of the [`ShadowJar`][ShadowJar] task to contain a `Class-Path` entry in the JAR manifest. The value of the `Class-Path` entry is the name of all dependencies resolved in the `shadow` configuration for the project. @@ -99,9 +93,9 @@ When deploying a shadowed JAR as an execution JAR, it is important to note that ## Configuring the JAR Manifest -Beyond the automatic configuration of the `Class-Path` entry, the `shadowJar` manifest is configured in a number of ways. -First, the manifest for the `shadowJar` task is configured to __inherit__ from the manifest of the standard `jar` task. -This means that any configuration performed on the `jar` task will propagate to the `shadowJar` tasks. +Beyond the automatic configuration of the `Class-Path` entry, the [`ShadowJar`][ShadowJar] manifest is configured in a number of ways. +First, the manifest for the [`ShadowJar`][ShadowJar] task is configured to __inherit__ from the manifest of the standard [`Jar`][Jar] task. +This means that any configuration performed on the [`Jar`][Jar] task will propagate to the [`ShadowJar`][ShadowJar] tasks. === "Kotlin" @@ -129,7 +123,7 @@ Inspecting the `META-INF/MANIFEST.MF` entry in the JAR file will reveal the foll Class-Path: /libs/a.jar ``` -If it is desired to inherit a manifest from a JAR task other than the standard `jar` task, the `inheritFrom` methods +If it is desired to inherit a manifest from a JAR task other than the standard [`Jar`][Jar] task, the `inheritFrom` methods on the `shadowJar.manifest` object can be used to configure the upstream. === "Kotlin" @@ -162,8 +156,7 @@ on the `shadowJar.manifest` object can be used to configure the upstream. ## Adding Extra Files -The `shadowJar` task is a subclass of the `Jar` task, which means that the -[Jar.from](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action)) +The [`ShadowJar`][ShadowJar] task is a subclass of the [`Jar`][Jar] task, which means that the[`Jar.from`][Jar.from] method can be used to add extra files. === "Kotlin" @@ -187,3 +180,19 @@ method can be used to add extra files. } } ``` + + + +[Jar.from]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action) +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html +[application]: https://docs.gradle.org/current/userguide/application_plugin.html +[archiveAppendix]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveAppendix +[archiveBaseName]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveBaseName +[archiveClassifier]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveClassifier +[archiveExtension]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveExtension +[archiveFileName]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveFileName +[archiveFile]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveFile +[archiveVersion]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:archiveVersion +[destinationDirectory]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:destinationDirectory +[maven-publish]: https://docs.gradle.org/current/userguide/publishing_maven.html diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 5b11295f6..fc91b96af 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -1,9 +1,8 @@ # Configuring Shadowed Dependencies -Shadow configures the default `shadowJar` task to merge all dependencies from the project's `runtimeClasspath` configuration -into the final JAR. -The configurations from which to source dependencies for the merging can be configured using the `configurations` property -of the [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task type. +Shadow configures the default [`ShadowJar`][ShadowJar] task to merge all dependencies from the project's `runtimeClasspath` configuration +into the final JAR. The configurations from which to source dependencies for the merging can be configured using the +[`configurations`][ShadowJar.configurations] property of the [`ShadowJar`][ShadowJar] task type. === "Kotlin" @@ -21,18 +20,18 @@ of the [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow } ``` -The above code sample would configure the `shadowJar` task to merge dependencies from only the `compileClasspath` configuration. +The above code sample would configure the [`ShadowJar`][ShadowJar] task to merge dependencies from only the `compileClasspath` configuration. This means any dependency declared in the `runtimeOnly` configuration would be **not** be included in the final JAR. -> Note the literal use of `project.configurations` when setting the `configurations` attribute of a -[`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. +> Note the literal use of [`project.configurations`][Project.configurations] when setting the +[`configurations`][ShadowJar.configurations] attribute of a [`ShadowJar`][ShadowJar] task. This is **required**. It may be tempting to specify `configurations = [configurations.compileClasspath]` but this will not -have the intended effect, as `configurations.compile` will try to delegate to the `configurations` property of the [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task instead of the `project` +have the intended effect, as `configurations.compile` will try to delegate to the +[`configurations`][ShadowJar.configurations] property of the [`ShadowJar`][ShadowJar] task instead of the `project` ## Embedding Jar Files Inside Your Shadow Jar -The `shadowJar` task is a subclass of the `Jar` task, which means that the -[Jar.from](https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action)) +The [`ShadowJar`][ShadowJar] task is a subclass of the [`Jar`][Jar] task, which means that the [`Jar.from`][Jar.from] method can be used to add extra files. === "Kotlin" @@ -72,12 +71,11 @@ See also [Adding Extra Files](../README.md#adding-extra-files) ## Filtering Dependencies Individual dependencies can be filtered from the final JAR by using the `dependencies` block of a -[`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task. -Dependency filtering does **not** apply to transitive dependencies. +[`ShadowJar`][ShadowJar] task. Dependency filtering does **not** apply to transitive dependencies. That is, excluding a dependency does not exclude any of its dependencies from the final JAR. The `dependency` blocks provides a number of methods for resolving dependencies using the notations familiar from -Gradle's `configurations` block. +Gradle's [`project.configurations`][Project.configurations] block. === "Kotlin" @@ -306,7 +304,7 @@ You can also use type-safe project accessors or version catalog accessors to fil ### Programmatically Selecting Dependencies to Filter If more complex decisions are needed to select the dependencies to be included, the -[`dependencies`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html) +[`ShadowJar.dependencies`][ShadowJar.dependencies] block provides a method that accepts a `Closure` for selecting dependencies. === "Kotlin" @@ -340,3 +338,12 @@ block provides a method that accepts a `Closure` for selecting dependencies. } } ``` + + + +[Jar.from]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action) +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[ShadowJar.configurations]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/configurations.html +[ShadowJar.dependencies]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html +[ShadowJar]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html +[Project.configurations]: https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:configurations diff --git a/docs/configuration/filtering/README.md b/docs/configuration/filtering/README.md index 767077298..f85dd1537 100644 --- a/docs/configuration/filtering/README.md +++ b/docs/configuration/filtering/README.md @@ -1,15 +1,11 @@ # Filtering Shadow Jar Contents The final contents of a shadow JAR can be filtered using the `exclude` and `include` methods inherited from Gradle's -`Jar` task type. +[`Jar`][Jar] task type. -Refer to the [Jar](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html) documentation for details -on the various versions of the methods and their behavior. - -When using `exclude`/`include` with a `ShadowJar` task, the resulting copy specs are applied to the _final_ JAR -contents. -This means that, the configuration is applied to the individual files from both the project source set or _any_ -of the dependencies to be merged. +When using `exclude`/`include` with a [`ShadowJar`][ShadowJar] task, the resulting copy specs are applied to the +_final_ JAR contents. This means that, the configuration is applied to the individual files from both the project +source set or _any_ of the dependencies to be merged. === "Kotlin" @@ -28,7 +24,7 @@ of the dependencies to be merged. ``` -Excludes and includes can be combined just like a normal `Jar` task, with `excludes` taking precedence over `includes`. +Excludes and includes can be combined just like a normal [`Jar`][Jar] task, with `excludes` taking precedence over `includes`. Additionally, ANT style patterns can be used to match multiple files. === "Kotlin" @@ -50,3 +46,8 @@ Additionally, ANT style patterns can be used to match multiple files. exclude 'a2.properties' } ``` + + + +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[ShadowJar]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index d5c821944..1882e310a 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -1,12 +1,10 @@ # Controlling JAR Content Merging Shadow allows for customizing the process by which the output JAR is generated through the -[`ResourceTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) interface. -This is a concept that has been carried over from the original Maven Shade implementation. -A [`ResourceTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) is invoked for each -entry in the JAR before being written to the final output JAR. -This allows a [`ResourceTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html) to -determine if it should process a particular entry and apply any modifications before writing the stream to the output. +[`ResourceTransformer`][ResourceTransformer] interface. This is a concept that has been carried over from the original +Maven Shade implementation. A [`ResourceTransformer`][ResourceTransformer] is invoked for each entry in the JAR before +being written to the final output JAR. This allows a [`ResourceTransformer`][ResourceTransformer] to determine if it +should process a particular entry and apply any modifications before writing the stream to the output. === "Kotlin" @@ -48,7 +46,7 @@ determine if it should process a particular entry and apply any modifications be } ``` -Additionally, a `ResourceTransformer` can accept a `Closure` to configure the provided `ResourceTransformer`. +Additionally, a [`ResourceTransformer`][ResourceTransformer] can accept a `Closure` to configure the provided `ResourceTransformer`. === "Kotlin" @@ -95,7 +93,7 @@ Additionally, a `ResourceTransformer` can accept a `Closure` to configure the pr } ``` -An instantiated instance of a `ResourceTransformer` can also be provided. +An instantiated instance of a [`ResourceTransformer`][ResourceTransformer] can also be provided. === "Kotlin" @@ -147,9 +145,8 @@ At runtime, this file is read and used to configure library or application behav Multiple dependencies may use the same service descriptor file name. In this case, it is generally desired to merge the content of each instance of the file into a single output file. -The [`ServiceFileTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) -class is used to perform this merging. By default, it will merge each copy of a file under `META-INF/services` into a -single file in the output JAR. +The [`ServiceFileTransformer`][ServiceFileTransformer] class is used to perform this merging. +By default, it will merge each copy of a file under `META-INF/services` into a single file in the output JAR. === "Kotlin" @@ -167,19 +164,17 @@ single file in the output JAR. } ``` -The above code snippet is a convenience syntax for calling -[`transform(ServiceFileTransformer.class)`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html). +The above code snippet is a convenience syntax for calling `transform(ServiceFileTransformer.class)`. > Groovy Extension Module descriptor files (located at `META-INF/services/org.codehaus.groovy.runtime.ExtensionModule`) -are ignored by the [`ServiceFileTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html). -This is due to these files having a different syntax than standard service descriptor files. -Use the [`mergeGroovyExtensionModules()`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/merge-groovy-extension-modules.html) method to merge -these files if your dependencies contain them. +> are ignored by the [`ServiceFileTransformer`][ServiceFileTransformer]. +> This is due to these files having a different syntax than standard service descriptor files. +> Use the [`mergeGroovyExtensionModules()`][mergeGroovyExtensionModules] method to merge +> these files if your dependencies contain them. ### Configuring the Location of Service Descriptor Files -By default, the [`ServiceFileTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) -is configured to merge files in `META-INF/services`. +By default, the [`ServiceFileTransformer`][ServiceFileTransformer] is configured to merge files in `META-INF/services`. This directory can be overridden to merge descriptor files in a different location. === "Kotlin" @@ -204,8 +199,8 @@ This directory can be overridden to merge descriptor files in a different locati ### Excluding/Including Specific Service Descriptor Files From Merging -The [`ServiceFileTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html) -class supports specifying specific files to include or exclude from merging. +The [`ServiceFileTransformer`][ServiceFileTransformer] class supports specifying specific files to include or exclude +from merging. === "Kotlin" @@ -231,10 +226,8 @@ class supports specifying specific files to include or exclude from merging. Shadow provides a specific transformer for dealing with Groovy extension module files. This is due to their special syntax and how they need to be merged together. -The [`GroovyExtensionModuleTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-groovy-extension-module-transformer/index.html) -will handle these files. -The [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task also provides a short syntax -method to add this transformer. +The [`GroovyExtensionModuleTransformer`][GroovyExtensionModuleTransformer] will handle these files. +The [`ShadowJar`][ShadowJar] task also provides a short syntax method to add this transformer. === "Kotlin" @@ -254,8 +247,11 @@ method to add this transformer. ## Merging Log4j2 Plugin Cache Files (`Log4j2Plugins.dat`) -`Log4j2PluginsCacheFileTransformer` is a `ResourceTransformer` that merges `META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat` plugin caches from all the jars -containing Log4j 2.x Core components. It's a Gradle equivalent of [Log4j Plugin Descriptor Transformer](https://logging.apache.org/log4j/transform/log4j-transform-maven-shade-plugin-extensions.html#log4j-plugin-cache-transformer). +[`Log4j2PluginsCacheFileTransformer`][Log4j2PluginsCacheFileTransformer] is a +[`ResourceTransformer`][ResourceTransformer] that merges +`META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat` plugin caches from all the jars +containing Log4j 2.x Core components. It's a Gradle equivalent of +[Log4j Plugin Descriptor Transformer](https://logging.apache.org/log4j/transform/log4j-transform-maven-shade-plugin-extensions.html#log4j-plugin-cache-transformer). === "Kotlin" @@ -275,13 +271,10 @@ containing Log4j 2.x Core components. It's a Gradle equivalent of [Log4j Plugin ## Appending Text Files -Generic text files can be appended together using the -[`AppendingTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html). +Generic text files can be appended together using the [`AppendingTransformer`][AppendingTransformer]. Each file is appended using separators (defaults to `\n`) to separate content. -The [`ShadowJar`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) task provides a short syntax -method of -[`append(String)`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html) to -configure this transformer. +The [`ShadowJar`][ShadowJar] task provides a short syntax method of [`append(String)`][ShadowJar.append] to configure +this transformer. === "Kotlin" @@ -330,11 +323,10 @@ configure this transformer. ## Appending XML Files -XML files require a special transformer for merging. -The [`XmlAppendingTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html) +XML files require a special transformer for merging. The [`XmlAppendingTransformer`][XmlAppendingTransformer] reads each XML document and merges each root element into a single document. -There is no short syntax method for the [`XmlAppendingTransformer`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html). -It must be added using the [`transform`](../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html)) methods. +There is no short syntax method for the [`XmlAppendingTransformer`][XmlAppendingTransformer]. +It must be added using the [`transform`][ShadowJar.transform] methods. === "Kotlin" @@ -358,9 +350,8 @@ It must be added using the [`transform`](../../api/shadow/com.github.jengelman.g ## Handling Duplicates Strategy -`ShadowJar` is a subclass of -[`org.gradle.api.tasks.AbstractCopyTask`](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html), -which means it honors the `duplicatesStrategy` property as its parent classes do. There are several strategies to handle: +`ShadowJar` is a subclass of [`org.gradle.api.tasks.AbstractCopyTask`][AbstractCopyTask], which means it honors the +`duplicatesStrategy` property as its parent classes do. There are several strategies to handle: - `EXCLUDE`: Do not allow duplicates by ignoring subsequent items to be created at the same path. - `FAIL`: Throw a `DuplicateFileCopyingException` when subsequent items are to be created at the same path. @@ -368,8 +359,7 @@ which means it honors the `duplicatesStrategy` property as its parent classes do - `INHERIT`: Uses the same strategy as the parent copy specification. - `WARN`: Do not attempt to prevent duplicates, but log a warning message when multiple items are to be created at the same path. -see more details about them in -[`DuplicatesStrategy`](https://docs.gradle.org/current/javadoc/org/gradle/api/file/DuplicatesStrategy.html). +see more details about them in [`DuplicatesStrategy`][DuplicatesStrategy]. `ShadowJar` recognizes `DuplicatesStrategy.INCLUDE` as the default, if you want to change the strategy, you can override it like: @@ -399,7 +389,7 @@ Different strategies will lead to different results for `foo/bar` files in the J - `WARN`: The **last** `foo/bar` file will be included in the final JAR, and a warning message will be logged. **NOTE:** The `duplicatesStrategy` takes precedence over transforming and relocating. If you mix the usages of -`duplicatesStrategy` and `ResourceTransformer` like below: +`duplicatesStrategy` and [`ResourceTransformer`][ResourceTransformer] like below: === "Kotlin" @@ -419,8 +409,25 @@ Different strategies will lead to different results for `foo/bar` files in the J } ``` -The `ServiceFileTransformer` will not work as expected because the `duplicatesStrategy` will exclude the duplicated -service files beforehand. However, this behavior might be what you expected for duplicated `foo/bar` files, preventing -them from being included. +The [`ServiceFileTransformer`][ServiceFileTransformer] will not work as expected because the `duplicatesStrategy` will +exclude the duplicated service files beforehand. However, this behavior might be what you expected for duplicated +`foo/bar` files, preventing them from being included. Want `ResourceTransformer`s and `duplicatesStrategy` to work together? There is a way to achieve this, leave the -`duplicatesStrategy` as `INCLUDE` and declare a custom `ResourceTransformer` to handle the duplicated files. +`duplicatesStrategy` as `INCLUDE` and declare a custom [`ResourceTransformer`][ResourceTransformer] to handle the +duplicated files. + + + +[AbstractCopyTask]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html +[AppendingTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html +[DuplicatesStrategy]: https://docs.gradle.org/current/javadoc/org/gradle/api/file/DuplicatesStrategy.html +[GroovyExtensionModuleTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-groovy-extension-module-transformer/index.html +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[Log4j2PluginsCacheFileTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-log4j2-plugins-cache-file-transformer/index.html +[ResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html +[ServiceFileTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html +[ShadowJar.append]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html +[ShadowJar.transform]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html +[ShadowJar]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html +[XmlAppendingTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html +[mergeGroovyExtensionModules]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/merge-groovy-extension-modules.html diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index 11e317959..782d825cd 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -1,9 +1,9 @@ # Creating a Custom ShadowJar Task -The built in `shadowJar` task only provides an output for the `main` source set of the project. -It is possible to add arbitrary [`ShadowJar`](../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html) -tasks to a project. When doing so, ensure that the `configurations` property is specified to inform Shadow which -dependencies to merge into the output. +The built in [`ShadowJar`][ShadowJar] task only provides an output for the `main` source set of the project. +It is possible to add arbitrary [`ShadowJar`][ShadowJar] tasks to a project. When doing so, ensure that the +[`configurations`][ShadowJar.configurations] property is specified to inform Shadow which dependencies to merge into +the output. === "Kotlin" @@ -56,3 +56,9 @@ dependencies to merge into the output. The code snippet above will generate a shadowed JAR containing both the `main` and `test` sources as well as all `testRuntimeOnly` and `testImplementation` dependencies. The file is output to `build/libs/--tests.jar`. + + + +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[ShadowJar.configurations]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/configurations.html +[ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 26005f93d..6b6e8b2ec 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -118,28 +118,27 @@ to have the desired effect. ## Default Java/Kotlin/Groovy Tasks -In the presence of the `java`, `org.jetbrains.kotlin.jvm` or `groovy` plugins -(that apply [JavaPlugin](https://docs.gradle.org/current/userguide/java_plugin.html) in their build logic), -Shadow will automatically configure the following behavior: +In the presence of the `java`, `org.jetbrains.kotlin.jvm` or `groovy` plugins (that apply [`JavaPlugin`][JavaPlugin] +in their build logic), Shadow will automatically configure the following behavior: -* Adds a `shadowJar` task to the project. +* Adds a [`ShadowJar`][ShadowJar] task to the project. * Adds a `shadow` configuration to the project. * Adds a `shadow` variant to the project. * Adds a `shadow` component to the project. -* Configures the `shadowJar` task to include all sources from the project's `main` sourceSet. -* Configures the `shadowJar` task to bundle all dependencies from the `runtimeClasspath` configuration. -* Configures the _classifier_ attribute of the `shadowJar` task to be `'all'` . -* Configures the `shadowJar` task to generate a `Manifest` with: - * Inheriting all configuration from the standard `jar` task. +* Configures the [`ShadowJar`][ShadowJar] task to include all sources from the project's `main` sourceSet. +* Configures the [`ShadowJar`][ShadowJar] task to bundle all dependencies from the `runtimeClasspath` configuration. +* Configures the _classifier_ attribute of the [`ShadowJar`][ShadowJar] task to be `'all'` . +* Configures the [`ShadowJar`][ShadowJar] task to generate a `Manifest` with: + * Inheriting all configuration from the standard [`Jar`][Jar] task. * Adds a `Class-Path` attribute to the `Manifest` that appends all dependencies from the `shadow` configuration -* Configures the `shadowJar` task to _exclude_ any JAR index or cryptographic signature files matching the following patterns: +* Configures the [`ShadowJar`][ShadowJar] task to _exclude_ any JAR index or cryptographic signature files matching the following patterns: * `META-INF/INDEX.LIST` * `META-INF/*.SF` * `META-INF/*.DSA` * `META-INF/*.RSA` * `META-INF/versions/**/module-info.class` * `module-info.class` -* Creates and registers the `shadow` component in the project (used for integrating with `maven-publish`). +* Creates and registers the `shadow` component in the project (used for integrating with [`maven-publish`][maven-publish]). ## ShadowJar Command Line options @@ -156,10 +155,17 @@ Here are the options that can be passed to the `shadowJar`: --rerun Causes the task to be re-run even if up-to-date ``` -Also, you can view more information about the `shadowJar` task by running the following command: +Also, you can view more information about the [`ShadowJar`][ShadowJar] task by running the following command: ```sh ./gradlew -q help --task shadowJar ``` Refer to [listing command line options](https://docs.gradle.org/current/userguide/custom_tasks.html#sec:listing_task_options). + + + +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[JavaPlugin]: https://docs.gradle.org/current/userguide/java_plugin.html +[maven-publish]: https://docs.gradle.org/current/userguide/publishing_maven.html +[ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html diff --git a/docs/gradle-plugins/README.md b/docs/gradle-plugins/README.md index ba5ee0a53..32798fea8 100644 --- a/docs/gradle-plugins/README.md +++ b/docs/gradle-plugins/README.md @@ -6,9 +6,9 @@ to relocate your dependencies to a different package name to avoid the collision Configuring the relocation has always been possible, but the build author is required to know all the package names beforehand. As of Shadow v8.1.0, automatic package relocation can be enabled by setting the `enabledRelocation` -and `relocationPrefix` settings on any `ShadowJar` task. +and `relocationPrefix` settings on any [`ShadowJar`][ShadowJar] task. -A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the `shadowJar` task for relocation. +A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the [`ShadowJar`][ShadowJar] task for relocation. === "Kotlin" @@ -59,17 +59,22 @@ A simple Gradle plugin can use this feature by applying the `shadow` plugin and ## Publishing shadowed Gradle plugins The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. Starting with this version, plugin projects that apply both Shadow and the Gradle Plugin Publish plugin will be -automatically configured to publish the output of the `shadowJar` tasks as the consumable artifact for the plugin. +automatically configured to publish the output of the [`ShadowJar`][ShadowJar] tasks as the consumable artifact for the plugin. See the [Gradle Plugin Publish docs](https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#shadow_dependencies) for details. ## Automatic package relocation with Shadow prior to v8.1.0 Prior to Shadow v8.1.0, Shadow handled this by introducing a new task type `ConfigureShadowRelocation`. -Tasks of this type are configured to target an instance of a `ShadowJar` task and run immediately before it. +Tasks of this type are configured to target an instance of a [`ShadowJar`][ShadowJar] task and run immediately before it. The `ConfigureShadowRelocation` task, scans the dependencies from the configurations specified on the associated `ShadowJar` task and collects the package names contained within them. It then configures relocation for these -packages using the specified `prefix` on the associated `ShadowJar` task. +packages using the specified `prefix` on the associated [`ShadowJar`][ShadowJar] task. While this is useful for developing Gradle plugins, nothing about the `ConfigureShadowRelocation` task is tied to Gradle projects. It can be used for standard Java or Groovy projects. + + + +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index 753b5cb25..aae18c2f4 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -65,9 +65,8 @@ easily organize your build logic for [Packaging Gradle Plugins](../gradle-plugin ## For 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 its `jvm` target. +Shadow honors Kotlin's [`org.jetbrains.kotlin.multiplatform`][org.jetbrains.kotlin.multiplatform] plugin and will +automatically configure additional tasks for bundling the shadowed JAR for its `jvm` target. === "Kotlin" @@ -144,3 +143,7 @@ configure additional tasks for bundling the shadowed JAR for its `jvm` target. } } ``` + + + +[org.jetbrains.kotlin.multiplatform]: https://kotlinlang.org/docs/multiplatform-intro.html diff --git a/docs/multi-project/README.md b/docs/multi-project/README.md index 4b2efeeeb..f961f44d3 100644 --- a/docs/multi-project/README.md +++ b/docs/multi-project/README.md @@ -2,7 +2,7 @@ When using Shadow in a multi-project build, project dependencies will be treated the same as external dependencies. -That is a project dependency will be merged into the `shadowJar` output of the project that +That is a project dependency will be merged into the [`ShadowJar`][ShadowJar] output of the project that is applying the Shadow plugin. ## Depending on the Shadow Jar from Another Project @@ -27,3 +27,8 @@ configuration of the shadowed project. implementation project(path: ':api', configuration: 'shadow') } ``` + + + +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 534c8b229..f264b4a17 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -3,7 +3,7 @@ ## Publishing with Maven-Publish Plugin The Shadow plugin will automatically configure the necessary tasks in the presence of Gradle's -`maven-publish` plugin. +[`maven-publish`][maven-publish] plugin. The plugin provides the `shadow` component to configure the publication with the necessary artifact and dependencies in the POM file. @@ -151,7 +151,8 @@ the `archiveClassifier` of the shadowed JAR like the following: ## Publish Custom ShadowJar Task Outputs -It is possible to publish a custom `ShadowJar` task's output via the [`MavenPublication.artifact(java.lang.Object)`](https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html#org.gradle.api.publish.maven.MavenPublication:artifact(java.lang.Object)) method. +It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via the +[`MavenPublication.artifact()`][MavenPublication.artifact] method. === "Kotlin" @@ -291,3 +292,10 @@ customizable properties listed in [Configuring Output Name](../configuration/REA We modified `archiveClassifier`, `archiveExtension` and `archiveBaseName` in this example, the published artifact will be named `my-artifact-2.0-my-classifier.my-ext` instead of `1.0-all.jar`. + + + +[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html +[MavenPublication.artifact]: https://docs.gradle.org/current/dsl/org.gradle.api.publish.maven.MavenPublication.html#org.gradle.api.publish.maven.MavenPublication:artifact(java.lang.Object) +[ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html +[maven-publish]: https://docs.gradle.org/current/userguide/publishing_maven.html From 87017967a06575bbca4aba3c401272a4af894338 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 16 Apr 2025 00:12:40 -0400 Subject: [PATCH 424/941] Reformat and rearrange docs (#1402) --- docs/README.md | 7 +- docs/about/README.md | 4 +- docs/changes/README.md | 555 +++++++++++------- docs/configuration/README.md | 23 +- docs/configuration/dependencies/README.md | 22 +- docs/configuration/filtering/README.md | 5 +- docs/configuration/merging/README.md | 14 +- docs/configuration/minimizing/README.md | 8 +- docs/configuration/relocation/README.md | 27 +- .../reproducible-builds/README.md | 13 +- docs/custom-tasks/README.md | 7 +- docs/getting-started/README.md | 14 +- docs/gradle-plugins/README.md | 15 +- docs/kotlin-plugins/README.md | 10 +- docs/publishing/README.md | 11 +- 15 files changed, 456 insertions(+), 279 deletions(-) diff --git a/docs/README.md b/docs/README.md index 311649150..d6b86a2ec 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,10 +6,9 @@ # Introduction Shadow is a Gradle plugin for combining a project's dependency classes and resources into a single -output Jar. -The combined Jar is often referred to a _fat-jar_ or _uber-jar_. -Shadow utilizes [`JarInputStream`][JarInputStream] and [`JarOutputStream`][JarOutputStream] to efficiently process dependent libraries -into the output jar without incurring the I/O overhead of expanding the jars to disk. +output Jar. The combined Jar is often referred to a _fat-jar_ or _uber-jar_. +Shadow utilizes [`JarInputStream`][JarInputStream] and [`JarOutputStream`][JarOutputStream] to efficiently process +dependent libraries into the output jar without incurring the I/O overhead of expanding the jars to disk. !!! warning "Plugin ID Change" diff --git a/docs/about/README.md b/docs/about/README.md index 898226a76..2bf0973e7 100644 --- a/docs/about/README.md +++ b/docs/about/README.md @@ -1,7 +1,7 @@ # About This Project -I (John Engelman) started this project in December 2012. We were working on converting from a monolithic application into the -new hot jazz of "microservices" using Dropwizard. +I (John Engelman) started this project in December 2012. We were working on converting from a monolithic application +into the new hot jazz of "microservices" using Dropwizard. I had also just started learning about Gradle and I knew that the incremental build system it provided would benefit our development team greatly. Unfortunately, the closest thing that Gradle had to Maven's Shade plugin was its ability to create application TARs and diff --git a/docs/changes/README.md b/docs/changes/README.md index 528075881..6e4ddbc35 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,18 +1,17 @@ # Change Log - ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta12...HEAD) - 2025-xx-xx **Fixed** - Avoid creating jvm targets eagerly for KMP. ([#1378](https://github.com/GradleUp/shadow/pull/1378)) - ## [9.0.0-beta12](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12) - 2025-04-01 **Added** - Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) + ``` Options: @@ -28,13 +27,13 @@ Options: - Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) - ## [9.0.0-beta11](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta11) - 2025-03-18 **Added** - 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)) +- Support using type-safe dependency accessors in + `ShadowJar.dependencies`. ([#1322](https://github.com/GradleUp/shadow/pull/1322)) - Set `Main-Class` attr for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) **Changed** @@ -43,30 +42,38 @@ Options: - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. - `ShadowSpec` no longer extends `CopySpec`. - Overload `relocate`, `transform` and things for better usability in Kotlin. -- **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) +- **BREAKING CHANGE:** Remove redundant types from function + returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) - `runShadow` no longer depends on `installShadowDist`. ([#1353](https://github.com/GradleUp/shadow/pull/1353)) **Fixed** -- Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) +- Fix relocation exclusion for file patterns like + `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) **Removed** -- **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) - +- **BREAKING CHANGE:** Reduce dependency and project overloads in + `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) ## [9.0.0-beta10](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta10) - 2025-03-05 **Added** - Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) - You still need to manually configure `manifest.attributes` (e.g. `Main-Class` attr) 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. **Changed** -- **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) - Aims to better align with the name of [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java) and to distinguish itself from [org.gradle.api.Transformer.java](https://docs.gradle.org/current/javadoc/org/gradle/api/Transformer.html). -- **BREAKING CHANGE:** Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) +- **BREAKING CHANGE:** Rename `Transformer` to + `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) + Aims to better align with the name + of [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java) + and to distinguish itself + from [org.gradle.api.Transformer.java](https://docs.gradle.org/current/javadoc/org/gradle/api/Transformer.html). +- **BREAKING CHANGE:** Mark `DefaultInheritManifest` as + `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) - Migrate doc sites to MkDocs. ([#1302](https://github.com/GradleUp/shadow/pull/1302)) **Fixed** @@ -75,8 +82,8 @@ Options: **Removed** -- **BREAKING CHANGE:** Remove `Named` from the parents of `Transformer`. ([#1289](https://github.com/GradleUp/shadow/pull/1289)) - +- **BREAKING CHANGE:** Remove `Named` from the parents of + `Transformer`. ([#1289](https://github.com/GradleUp/shadow/pull/1289)) ## [9.0.0-beta9](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta9) - 2025-02-24 @@ -86,10 +93,13 @@ Options: **Changed** -- **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) +- **BREAKING CHANGE:** Move tracking unused classes logic out of + `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) - Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) -- **BREAKING CHANGE:** Move `DependencyFilter` from `com.github.jengelman.gradle.plugins.shadow.internal` into `com.github.jengelman.gradle.plugins.shadow.tasks`. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) -- Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- **BREAKING CHANGE:** Move `DependencyFilter` from `com.github.jengelman.gradle.plugins.shadow.internal` into + `com.github.jengelman.gradle.plugins.shadow.tasks`. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) +- Refactor file visiting logic in `StreamAction`, handle file unzipping via + `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) **Fixed** @@ -100,15 +110,16 @@ Options: - **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) - **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) -- **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - +- **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and + `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) ## [9.0.0-beta8](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8) - 2025-02-08 **Added** - Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) -- Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) +- Inject `Multi-Release` manifest attribute if any dependency contains + it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) **Changed** @@ -117,15 +128,16 @@ Options: **Removed** -- **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) +- **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been + removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) - **BREAKING CHANGE:** Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) - ## [9.0.0-beta7](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta7) - 2025-02-02 **Added** -- Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) +- Inject `TargetJvmVersion` attribute for Gradle Module + Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) - Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) **Changed** @@ -139,23 +151,25 @@ Options: **Fixed** - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) -- Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) +- Fix `ShadowJar` not being successful after `includes` or `excludes` are + changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) **Removed** -- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) - +- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` + task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) ## [9.0.0-beta6](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta6) - 2025-01-23 **Added** -- Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) +- Exclude `module-info.class` in Multi-Release folders by + default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) **Fixed** -- Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) - +- Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` + files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) ## [9.0.0-beta5](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta5) - 2025-01-21 @@ -166,29 +180,31 @@ Options: **Changed** - Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) -- **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) +- **BREAKING CHANGE:** Migrate all `ListProperty` usages to + `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) Some public `List` parameters are also changed to `Set`. -- Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) +- Replace deprecated `SelfResolvingDependency` with + `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) - Support configuring `separator` in `AppendingTransformer`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) - This is useful for handling files like `resources/application.yml`. + This is useful for handling files like `resources/application.yml`. **Fixed** -- Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) - +- Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) ## [9.0.0-beta4](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta4) - 2024-12-06 **Changed** -- **BREAKING CHANGE:** Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as `SetProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) +- **BREAKING CHANGE:** Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as + `SetProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) **Fixed** -- Adjust property initializations and modifiers in `ShadowJar`. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) +- Adjust property initializations and modifiers in + `ShadowJar`. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) This fixes the regression for registering custom `ShadowJar` tasks. - ## [9.0.0-beta2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta2) - 2024-11-28 **Fixed** @@ -196,7 +212,6 @@ Options: - Revert "Migrate SimpleRelocator to using lazy properties" ([#1052](https://github.com/GradleUp/shadow/pull/1052)) This fixes the relocation not working in `v9.0.0-beta1`. - ## [9.0.0-beta1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta1) - 2024-11-27 **Added** @@ -206,11 +221,15 @@ Options: **Changed** - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) -- **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) -- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) +- **BREAKING CHANGE:** Migrate `Transformer`s to using lazy + properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) +- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy + properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) `isEnableRelocation` is removed, use `enableRelocation` instead. -- **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) -- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) +- **BREAKING CHANGE:** Resolve `Configuration` directly in + `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) +- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy + properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) **Removed** @@ -218,8 +237,7 @@ Options: **Fixed** -- Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) - +- Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) ## [8.3.6](https://github.com/GradleUp/shadow/releases/tag/8.3.6) - 2025-02-02 @@ -227,20 +245,20 @@ Options: - Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) - ## [8.3.5](https://github.com/GradleUp/shadow/releases/tag/8.3.5) - 2024-11-03 **Fixed** - Revert "Bump Java level to 11" ([#1011](https://github.com/GradleUp/shadow/issues/1011)). - This reverts the change to maintain compatibility with 8.x versions. The Java level will be bumped to 11 or above in the next major release. - + This reverts the change to maintain compatibility with 8.x versions. The Java level will be bumped to 11 or above in + the next major release. ## [8.3.4](https://github.com/GradleUp/shadow/releases/tag/8.3.4) - 2024-10-29 **Fixed** -- Apply legacy plugin last, and declare capabilities for old plugins, fixes [#964](https://github.com/GradleUp/shadow/issues/964). ([#991](https://github.com/GradleUp/shadow/pull/991)) +- Apply legacy plugin last, and declare capabilities for old plugins, + fixes [#964](https://github.com/GradleUp/shadow/issues/964). ([#991](https://github.com/GradleUp/shadow/pull/991)) ## [8.3.3](https://github.com/GradleUp/shadow/releases/tag/8.3.3) - 2024-10-02 @@ -248,7 +266,6 @@ Options: - Disable Develocity integration by default. ([#993](https://github.com/GradleUp/shadow/pull/993)) - ## [8.3.2](https://github.com/GradleUp/shadow/releases/tag/8.3.2) - 2024-09-18 **Added** @@ -257,14 +274,16 @@ Options: **Changed** -- `ShadowExtension.component` has been deprecated, now you can use `component.shadow` instead. ([#956](https://github.com/GradleUp/shadow/pull/956)) -- **BREAKING CHANGE:** update to [jdependency 2.11](https://github.com/tcurdt/jdependency/releases/tag/jdependency-2.11), this requires Java 11 or above to run. ([#974](https://github.com/GradleUp/shadow/pull/974)) +- `ShadowExtension.component` has been deprecated, now you can use `component.shadow` + instead. ([#956](https://github.com/GradleUp/shadow/pull/956)) +- **BREAKING CHANGE:** update + to [jdependency 2.11](https://github.com/tcurdt/jdependency/releases/tag/jdependency-2.11), this requires Java 11 or + above to run. ([#974](https://github.com/GradleUp/shadow/pull/974)) **Fixed** - Stop publishing Shadow self fat jar to Maven repository. ([#967](https://github.com/GradleUp/shadow/pull/967)) - ## [8.3.1](https://github.com/GradleUp/shadow/releases/tag/8.3.1) - 2024-09-10 **Added** @@ -277,14 +296,13 @@ Options: - Explicitly add classifier to maven publication. ([#904](https://github.com/GradleUp/shadow/pull/904)) - Refix excluding Gradle APIs for java-gradle-plugin. ([#948](https://github.com/GradleUp/shadow/pull/948)) - ## [8.3.0](https://github.com/GradleUp/shadow/releases/tag/8.3.0) - 2024-08-08 **Changed** -- **BREAKING CHANGE:** the GitHub has been transferred from `johnrengelman/shadow` to `GradleUp/shadow`, you can view +- **BREAKING CHANGE:** the GitHub has been transferred from `johnrengelman/shadow` to `GradleUp/shadow`, you can view more details in [GradleUp/shadow/issues/908](https://github.com/GradleUp/shadow/issues/908). - We also update the plugin ID from `com.github.johnrengelman.shadow` to `com.gradleup.shadow`, and the + We also update the plugin ID from `com.github.johnrengelman.shadow` to `com.gradleup.shadow`, and the Maven coordinate from `com.github.johnrengelman:shadow` to `com.gradleup.shadow:shadow-gradle-plugin`. - Bump the min Gradle requirement from `8.0.0` to `8.3`. ([#876](https://github.com/GradleUp/shadow/pull/876)) - Support Java 21. ([#876](https://github.com/GradleUp/shadow/pull/876)) @@ -295,83 +313,128 @@ Options: - Fix for PropertiesFileTransformer breaks Reproducible builds in `8.1.1`. ([#858](https://github.com/GradleUp/shadow/pull/858)) - ## [8.1.1](https://github.com/GradleUp/shadow/releases/tag/8.1.1) - 2023-03-20 -**NOTE:** As of this version, the GitHub repository has migrated to the `main` branch as the default branch for releases. +**NOTE:** As of this version, the GitHub repository has migrated to the `main` branch as the default branch for +releases. ### What's Changed -* Replace deprecated ConfigureUtil by [@Goooler](https://github.com/Goooler) in [#826](https://github.com/GradleUp/shadow/pull/826) -* Polish outdated configs by [@Goooler](https://github.com/Goooler) in [#831](https://github.com/GradleUp/shadow/pull/831) -* Update plugin com.gradle.enterprise to v3.12.5 by [@renovate](https://github.com/renovate-bot) in [#838](https://github.com/GradleUp/shadow/pull/838) -* Update dependency gradle to v8.0.2 by [@renovate](https://github.com/renovate-bot) in [#844](https://github.com/GradleUp/shadow/pull/844) -* fix(deps): update dependency org.codehaus.plexus:plexus-utils to v3.5.1 by [@renovate](https://github.com/renovate-bot) in [#837](https://github.com/GradleUp/shadow/pull/837) -* chore(deps): update dependency prismjs to v1.27.0 [security] by [@renovate](https://github.com/renovate-bot) in [#828](https://github.com/GradleUp/shadow/pull/828) -* Encode transformed properties files with specified Charset by [@scottsteen](https://github.com/scottsteen) in [#819](https://github.com/GradleUp/shadow/pull/819) -* chore(deps): update dependency vuepress to v1.9.9 by [@renovate](https://github.com/renovate-bot) in [#842](https://github.com/GradleUp/shadow/pull/842) + +* Replace deprecated ConfigureUtil by [@Goooler](https://github.com/Goooler) + in [#826](https://github.com/GradleUp/shadow/pull/826) +* Polish outdated configs by [@Goooler](https://github.com/Goooler) + in [#831](https://github.com/GradleUp/shadow/pull/831) +* Update plugin com.gradle.enterprise to v3.12.5 by [@renovate](https://github.com/renovate-bot) + in [#838](https://github.com/GradleUp/shadow/pull/838) +* Update dependency gradle to v8.0.2 by [@renovate](https://github.com/renovate-bot) + in [#844](https://github.com/GradleUp/shadow/pull/844) +* fix(deps): update dependency org.codehaus.plexus:plexus-utils to v3.5.1 + by [@renovate](https://github.com/renovate-bot) in [#837](https://github.com/GradleUp/shadow/pull/837) +* chore(deps): update dependency prismjs to v1.27.0 [security] by [@renovate](https://github.com/renovate-bot) + in [#828](https://github.com/GradleUp/shadow/pull/828) +* Encode transformed properties files with specified Charset by [@scottsteen](https://github.com/scottsteen) + in [#819](https://github.com/GradleUp/shadow/pull/819) +* chore(deps): update dependency vuepress to v1.9.9 by [@renovate](https://github.com/renovate-bot) + in [#842](https://github.com/GradleUp/shadow/pull/842) ### New Contributors -* [@renovate](https://github.com/renovate-bot) made their first contribution in [#838](https://github.com/GradleUp/shadow/pull/838) -* [@scottsteen](https://github.com/scottsteen) made their first contribution in [#819](https://github.com/GradleUp/shadow/pull/819) -**Full Changelog**: [`8.1.0...8.1.1`](https://github.com/GradleUp/shadow/compare/8.1.0...8.1.1) +* [@renovate](https://github.com/renovate-bot) made their first contribution + in [#838](https://github.com/GradleUp/shadow/pull/838) +* [@scottsteen](https://github.com/scottsteen) made their first contribution + in [#819](https://github.com/GradleUp/shadow/pull/819) +**Full Changelog**: [`8.1.0...8.1.1`](https://github.com/GradleUp/shadow/compare/8.1.0...8.1.1) ## [8.1.0](https://github.com/GradleUp/shadow/releases/tag/8.1.0) - 2023-02-26 -**BREAKING CHANGE:** Due to adoption of the latest version of the `com.gradle.plugin-publish` plugin, the maven GAV coordinates have changed as of this version. -The correct coordinates now align with the plugin ID itself: `group=com.github.johnrengelman, artifact=shadow, version=`. +**BREAKING CHANGE:** Due to adoption of the latest version of the `com.gradle.plugin-publish` plugin, the maven GAV +coordinates have changed as of this version. +The correct coordinates now align with the plugin ID itself: +`group=com.github.johnrengelman, artifact=shadow, version=`. For example, `classpath("com.github.johnrengelman:shadow:8.1.0")` is the correct configuration for this version. -**BREAKING CHANGE:** The `ConfigureShadowRelocation` task was removed as of this version to better support Gradle configuration caching. -Instead, use the `enableRelocation = true` and `relocationPrefix = ""` settings on the `ShadowJar` task type. +**BREAKING CHANGE:** The `ConfigureShadowRelocation` task was removed as of this version to better support Gradle +configuration caching. +Instead, use the `enableRelocation = true` and `relocationPrefix = ""` settings on the `ShadowJar` task +type. ### What's Changed + * Minor cleanups by [@Goooler](https://github.com/Goooler) in [#823](https://github.com/GradleUp/shadow/pull/823) * Support config cache by [@Goooler](https://github.com/Goooler) in [#824](https://github.com/GradleUp/shadow/pull/824) -* Fix RelocatorRemapper: do not map inner class name if not changed by [@Him188](https://github.com/Him188) in [#793](https://github.com/GradleUp/shadow/pull/793) +* Fix RelocatorRemapper: do not map inner class name if not changed by [@Him188](https://github.com/Him188) + in [#793](https://github.com/GradleUp/shadow/pull/793) ### New Contributors -* [@Him188](https://github.com/Him188) made their first contribution in [#793](https://github.com/GradleUp/shadow/pull/793) -**Full Changelog**: [`8.0.0...8.1.0`](https://github.com/GradleUp/shadow/compare/8.0.0...8.1.0) +* [@Him188](https://github.com/Him188) made their first contribution + in [#793](https://github.com/GradleUp/shadow/pull/793) +**Full Changelog**: [`8.0.0...8.1.0`](https://github.com/GradleUp/shadow/compare/8.0.0...8.1.0) ## [8.0.0](https://github.com/GradleUp/shadow/releases/tag/8.0.0) - 2023-02-24 ### What's Changed -* Fix the plugin dependency identifier in the docs by [@lnhrdt](https://github.com/lnhrdt) in [#754](https://github.com/GradleUp/shadow/pull/754) -* mergeGroovyExtensionModules() not working with Groovy 2.5+ by [@paulk-asert](https://github.com/paulk-asert) in [#779](https://github.com/GradleUp/shadow/pull/779) -* Upgrade to ASM 9.3 to support JDK 19. by [@vyazelenko](https://github.com/vyazelenko) in [#770](https://github.com/GradleUp/shadow/pull/770) -* Do not add a dependencies block if it's already there by [@desiderantes](https://github.com/desiderantes) in [#769](https://github.com/GradleUp/shadow/pull/769) -* Update README with new badge and links by [@ThexXTURBOXx](https://github.com/ThexXTURBOXx) in [#743](https://github.com/GradleUp/shadow/pull/743) -* Fix value not set when rawString is true. by [@qian0817](https://github.com/qian0817) in [#765](https://github.com/GradleUp/shadow/pull/765) -* Mark the Log4j2PluginsCacheFileTransformer as cacheable. by [@staktrace](https://github.com/staktrace) in [#724](https://github.com/GradleUp/shadow/pull/724) -* Fix retrieval of dependencies node when publishing by [@netomi](https://github.com/netomi) in [#798](https://github.com/GradleUp/shadow/pull/798) -* Upgrade dependency ASM from `9.3` to `9.4` by [@codecholeric](https://github.com/codecholeric) in [#817](https://github.com/GradleUp/shadow/pull/817) -* Fix a typo of code comment in the minimizing page by [@jebnix](https://github.com/jebnix) in [#800](https://github.com/GradleUp/shadow/pull/800) -* Prefer using plugin extensions over deprecated conventions by [@eskatos](https://github.com/eskatos) in [#821](https://github.com/GradleUp/shadow/pull/821) -* Introduce CleanProperties by [@simPod](https://github.com/simPod) in [#622](https://github.com/GradleUp/shadow/pull/622) + +* Fix the plugin dependency identifier in the docs by [@lnhrdt](https://github.com/lnhrdt) + in [#754](https://github.com/GradleUp/shadow/pull/754) +* mergeGroovyExtensionModules() not working with Groovy 2.5+ by [@paulk-asert](https://github.com/paulk-asert) + in [#779](https://github.com/GradleUp/shadow/pull/779) +* Upgrade to ASM 9.3 to support JDK 19. by [@vyazelenko](https://github.com/vyazelenko) + in [#770](https://github.com/GradleUp/shadow/pull/770) +* Do not add a dependencies block if it's already there by [@desiderantes](https://github.com/desiderantes) + in [#769](https://github.com/GradleUp/shadow/pull/769) +* Update README with new badge and links by [@ThexXTURBOXx](https://github.com/ThexXTURBOXx) + in [#743](https://github.com/GradleUp/shadow/pull/743) +* Fix value not set when rawString is true. by [@qian0817](https://github.com/qian0817) + in [#765](https://github.com/GradleUp/shadow/pull/765) +* Mark the Log4j2PluginsCacheFileTransformer as cacheable. by [@staktrace](https://github.com/staktrace) + in [#724](https://github.com/GradleUp/shadow/pull/724) +* Fix retrieval of dependencies node when publishing by [@netomi](https://github.com/netomi) + in [#798](https://github.com/GradleUp/shadow/pull/798) +* Upgrade dependency ASM from `9.3` to `9.4` by [@codecholeric](https://github.com/codecholeric) + in [#817](https://github.com/GradleUp/shadow/pull/817) +* Fix a typo of code comment in the minimizing page by [@jebnix](https://github.com/jebnix) + in [#800](https://github.com/GradleUp/shadow/pull/800) +* Prefer using plugin extensions over deprecated conventions by [@eskatos](https://github.com/eskatos) + in [#821](https://github.com/GradleUp/shadow/pull/821) +* Introduce CleanProperties by [@simPod](https://github.com/simPod) + in [#622](https://github.com/GradleUp/shadow/pull/622) * Support Gradle 8.0 by [@Goooler](https://github.com/Goooler) in [#822](https://github.com/GradleUp/shadow/pull/822) -* Updated dependencies, Gradle versions and Fix Test by [@ElisaMin](https://github.com/ElisaMin) in [#791](https://github.com/GradleUp/shadow/pull/791) +* Updated dependencies, Gradle versions and Fix Test by [@ElisaMin](https://github.com/ElisaMin) + in [#791](https://github.com/GradleUp/shadow/pull/791) ### New Contributors -* [@lnhrdt](https://github.com/lnhrdt) made their first contribution in [#754](https://github.com/GradleUp/shadow/pull/754) -* [@paulk-asert](https://github.com/paulk-asert) made their first contribution in [#779](https://github.com/GradleUp/shadow/pull/779) -* [@desiderantes](https://github.com/desiderantes) made their first contribution in [#769](https://github.com/GradleUp/shadow/pull/769) -* [@ThexXTURBOXx](https://github.com/ThexXTURBOXx) made their first contribution in [#743](https://github.com/GradleUp/shadow/pull/743) -* [@qian0817](https://github.com/qian0817) made their first contribution in [#765](https://github.com/GradleUp/shadow/pull/765) -* [@staktrace](https://github.com/staktrace) made their first contribution in [#724](https://github.com/GradleUp/shadow/pull/724) -* [@netomi](https://github.com/netomi) made their first contribution in [#798](https://github.com/GradleUp/shadow/pull/798) -* [@codecholeric](https://github.com/codecholeric) made their first contribution in [#817](https://github.com/GradleUp/shadow/pull/817) -* [@jebnix](https://github.com/jebnix) made their first contribution in [#800](https://github.com/GradleUp/shadow/pull/800) -* [@eskatos](https://github.com/eskatos) made their first contribution in [#821](https://github.com/GradleUp/shadow/pull/821) -* [@simPod](https://github.com/simPod) made their first contribution in [#622](https://github.com/GradleUp/shadow/pull/622) -* [@Goooler](https://github.com/Goooler) made their first contribution in [#822](https://github.com/GradleUp/shadow/pull/822) -* [@ElisaMin](https://github.com/ElisaMin) made their first contribution in [#791](https://github.com/GradleUp/shadow/pull/791) -**Full Changelog**: [`7.1.2...8.0.0`](https://github.com/GradleUp/shadow/compare/7.1.2...8.0.0) +* [@lnhrdt](https://github.com/lnhrdt) made their first contribution + in [#754](https://github.com/GradleUp/shadow/pull/754) +* [@paulk-asert](https://github.com/paulk-asert) made their first contribution + in [#779](https://github.com/GradleUp/shadow/pull/779) +* [@desiderantes](https://github.com/desiderantes) made their first contribution + in [#769](https://github.com/GradleUp/shadow/pull/769) +* [@ThexXTURBOXx](https://github.com/ThexXTURBOXx) made their first contribution + in [#743](https://github.com/GradleUp/shadow/pull/743) +* [@qian0817](https://github.com/qian0817) made their first contribution + in [#765](https://github.com/GradleUp/shadow/pull/765) +* [@staktrace](https://github.com/staktrace) made their first contribution + in [#724](https://github.com/GradleUp/shadow/pull/724) +* [@netomi](https://github.com/netomi) made their first contribution + in [#798](https://github.com/GradleUp/shadow/pull/798) +* [@codecholeric](https://github.com/codecholeric) made their first contribution + in [#817](https://github.com/GradleUp/shadow/pull/817) +* [@jebnix](https://github.com/jebnix) made their first contribution + in [#800](https://github.com/GradleUp/shadow/pull/800) +* [@eskatos](https://github.com/eskatos) made their first contribution + in [#821](https://github.com/GradleUp/shadow/pull/821) +* [@simPod](https://github.com/simPod) made their first contribution + in [#622](https://github.com/GradleUp/shadow/pull/622) +* [@Goooler](https://github.com/Goooler) made their first contribution + in [#822](https://github.com/GradleUp/shadow/pull/822) +* [@ElisaMin](https://github.com/ElisaMin) made their first contribution + in [#791](https://github.com/GradleUp/shadow/pull/791) +**Full Changelog**: [`7.1.2...8.0.0`](https://github.com/GradleUp/shadow/compare/7.1.2...8.0.0) ## [7.1.2](https://github.com/GradleUp/shadow/releases/tag/7.1.2) - 2021-12-28 @@ -383,23 +446,36 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = "]` syntax [#536](https://github.com/GradleUp/shadow/pull/536) +- Fix to generated start script to correctly use + `optsEnvironmentVar`[#518](https://github.com/GradleUp/shadow/commit/7e99c02957773205c3babdd23f4bbf883330c975) +- [Yahor Berdnikau](https://github.com/Tapchicoma) - Fix issues with Gradle API being embedded into published + JAR [#527](https://github.com/GradleUp/shadow/issues/527) +- [Dmitry Vyazelenko](https://github.com/vyazelenko) - ASM updates to support latest Java + versions [#549](https://github.com/GradleUp/shadow/pull/549) +- [ejjcase](https://github.com/ejjcase) - Support exposing shadowed project dependencies via + POM [#543](https://github.com/GradleUp/shadow/pull/543) +- [Artem Chubaryan](https://github.com/Armaxis) - Performance + optimizations [#535](https://github.com/GradleUp/shadow/pull/535) +- [Trask Stalnaker](https://github.com/trask) - Fix exclude patterns on + Windows [#539](https://github.com/GradleUp/shadow/pull/539) +- [Artem Chubaryan](https://github.com/Armaxis) - Allow usage of true regex patterns for include/exclude by the + `%regex[]` syntax [#536](https://github.com/GradleUp/shadow/pull/536) ## [5.2.0](https://github.com/GradleUp/shadow/releases/tag/5.2.0) - 2019-11-10 -- [Inez Korczyński](https://github.com/inez) - Performance optimization when evaluating relocation paths [#507](https://github.com/GradleUp/shadow/pull/507) -- [Jeff Adler](https://github.com/jeffalder) - Fix remapping issues with multi release JARS [#526](https://github.com/GradleUp/shadow/pull/526) -- [Gary Hale](https://github.com/ghale) - Implement support for Gradle build cache [#524](https://github.com/GradleUp/shadow/pull/524) -- [Roberto Perez Alcolea](https://github.com/rpalcolea) - Gradle 6.x support [#517](https://github.com/GradleUp/shadow/pull/517) -- [Konstantin Gribov](https://github.com/grossws) - Return support for 5.0 for convention mapping [#502](https://github.com/GradleUp/shadow/pull/502) -- [Lai Jiang](https://github.com/jianglai) - Documentation updates on how to reconfigure `classifier` and `version` [#512](https://github.com/GradleUp/shadow/pull/512) +- [Inez Korczyński](https://github.com/inez) - Performance optimization when evaluating relocation + paths [#507](https://github.com/GradleUp/shadow/pull/507) +- [Jeff Adler](https://github.com/jeffalder) - Fix remapping issues with multi release + JARS [#526](https://github.com/GradleUp/shadow/pull/526) +- [Gary Hale](https://github.com/ghale) - Implement support for Gradle build + cache [#524](https://github.com/GradleUp/shadow/pull/524) +- [Roberto Perez Alcolea](https://github.com/rpalcolea) - Gradle 6.x + support [#517](https://github.com/GradleUp/shadow/pull/517) +- [Konstantin Gribov](https://github.com/grossws) - Return support for 5.0 for convention + mapping [#502](https://github.com/GradleUp/shadow/pull/502) +- [Lai Jiang](https://github.com/jianglai) - Documentation updates on how to reconfigure `classifier` and + `version` [#512](https://github.com/GradleUp/shadow/pull/512) ## [5.1.0](https://github.com/GradleUp/shadow/releases/tag/5.1.0) - 2019-06-29 -- [Chris Rankin](https://github.com/chrisr3) - Add `ManifestAppenderTransformer` to support appending to Jar manifest [#474](https://github.com/GradleUp/shadow/pull/474) -- [Min-Ken Lai](https://github.com/minkenlai) - Additional escaping fixes in start script [#487](https://github.com/GradleUp/shadow/pull/487) -- [Alan D. Cabrera](https://github.com/maguro) - Automatically remove `gradleApi` from `compile` scope in the presence of `shadow` [#459](https://github.com/GradleUp/shadow/pull/459) -- [Christian Stein](https://github.com/sormuras) - Do not initialize `UnusedTracker` when not requested [#480](https://github.com/GradleUp/shadow/pull/480), [#479](https://github.com/GradleUp/shadow/issues/479) -- [Attila Kelemen](https://github.com/kelemen) - Fix `NullPointerException` when using java minimization and api project dependency with version [#477](https://github.com/GradleUp/shadow/pull/477) +- [Chris Rankin](https://github.com/chrisr3) - Add `ManifestAppenderTransformer` to support appending to Jar + manifest [#474](https://github.com/GradleUp/shadow/pull/474) +- [Min-Ken Lai](https://github.com/minkenlai) - Additional escaping fixes in start + script [#487](https://github.com/GradleUp/shadow/pull/487) +- [Alan D. Cabrera](https://github.com/maguro) - Automatically remove `gradleApi` from `compile` scope in the presence + of `shadow` [#459](https://github.com/GradleUp/shadow/pull/459) +- [Christian Stein](https://github.com/sormuras) - Do not initialize `UnusedTracker` when not + requested [#480](https://github.com/GradleUp/shadow/pull/480), [#479](https://github.com/GradleUp/shadow/issues/479) +- [Attila Kelemen](https://github.com/kelemen) - Fix `NullPointerException` when using java minimization and api project + dependency with version [#477](https://github.com/GradleUp/shadow/pull/477) ## [5.0.0](https://github.com/GradleUp/shadow/releases/tag/5.0.0) - 2019-02-28 - Require Gradle 5.0+ - Fix issue with build classifier `-all` being dropped in Gradle 5.1+ -- [Roberto Perez Alcolea](https://github.com/rpalcolea) - Exclude project dependencies from minimization [#420](https://github.com/GradleUp/shadow/pull/420) -- [Matt King](https://github.com/kyrrigle), [Richard Marbach](https://github.com/RichardMarbach) - Fix escaping in start script [#453](https://github.com/GradleUp/shadow/pull/454), [#455](https://github.com/GradleUp/shadow/pull/455) -- [Dennis Schumann](https://github.com/Hillkorn) - Fix Gradle 5.2 incompatibility with `ShadowJar.getMetaClass()` [#456](https://github.com/GradleUp/shadow/pull/456) -- [Brane F. Gračnar](https://github.com/bfg) - Fix compatibility with `com.palantir.docker` [#460](https://github.com/GradleUp/shadow/pull/460) +- [Roberto Perez Alcolea](https://github.com/rpalcolea) - Exclude project dependencies from + minimization [#420](https://github.com/GradleUp/shadow/pull/420) +- [Matt King](https://github.com/kyrrigle), [Richard Marbach](https://github.com/RichardMarbach) - Fix escaping in start + script [#453](https://github.com/GradleUp/shadow/pull/454), [#455](https://github.com/GradleUp/shadow/pull/455) +- [Dennis Schumann](https://github.com/Hillkorn) - Fix Gradle 5.2 incompatibility with + `ShadowJar.getMetaClass()` [#456](https://github.com/GradleUp/shadow/pull/456) +- [Brane F. Gračnar](https://github.com/bfg) - Fix compatibility with + `com.palantir.docker` [#460](https://github.com/GradleUp/shadow/pull/460) ## [4.0.4](https://github.com/GradleUp/shadow/releases/tag/4.0.4) - 2019-01-19 - When using `shadow`, `application`, and `maven` plugins together, remove `shadowDistZip` and `shadowDistTar` from `configurations.archives` so they are not published or installed by default with the `uploadArchives` or `install` tasks. [#347](https://github.com/GradleUp/shadow/issues/347) -- [James Nelson](https://github.com/JamesXNelson) - Fix `null` path when using Jar minimization and Gradle's `api` configuration. [#424](https://github.com/GradleUp/shadow/issues/424), [#425](https://github.com/GradleUp/shadow/issues/425) +- [James Nelson](https://github.com/JamesXNelson) - Fix `null` path when using Jar minimization and Gradle's `api` + configuration. [#424](https://github.com/GradleUp/shadow/issues/424), [#425](https://github.com/GradleUp/shadow/issues/425) ## [4.0.3](https://github.com/GradleUp/shadow/releases/tag/4.0.3) - 2018-11-21 -- [Mark Vieira](https://github.com/mark-vieira) - Don't leak plugin classes to Gradle's Spec cache [#430](https://github.com/GradleUp/shadow/pull/430) +- [Mark Vieira](https://github.com/mark-vieira) - Don't leak plugin classes to Gradle's Spec + cache [#430](https://github.com/GradleUp/shadow/pull/430) ## [4.0.2](https://github.com/GradleUp/shadow/releases/tag/4.0.2) - 2018-10-27 -- [Petar Petrov](https://github.com/petarov) - Update to ASM 7.0-beta and jdependency 2.1.1 to support Java 11, [#415](https://github.com/GradleUp/shadow/pull/415) -- [Victor Tso](https://github.com/roxchkplusony) - Ensure input streams are closed, [#411](https://github.com/GradleUp/shadow/pull/411) -- [Osip Fatkullin](https://github.com/osipxd) - Exclude `api` configuration from minimization, [#405](https://github.com/GradleUp/shadow/pull/405) +- [Petar Petrov](https://github.com/petarov) - Update to ASM 7.0-beta and jdependency 2.1.1 to support Java + 11, [#415](https://github.com/GradleUp/shadow/pull/415) +- [Victor Tso](https://github.com/roxchkplusony) - Ensure input streams are + closed, [#411](https://github.com/GradleUp/shadow/pull/411) +- [Osip Fatkullin](https://github.com/osipxd) - Exclude `api` configuration from + minimization, [#405](https://github.com/GradleUp/shadow/pull/405) ## [4.0.1](https://github.com/GradleUp/shadow/releases/tag/4.0.1) - 2018-09-30 -- **Breaking Change!** `Transform.modifyOutputStream(ZipOutputStream os)` to `Transform.modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps)`. - Typically breaking changes are reserved for major version releases, but this change was necessary for `preserverFileTimestamps` (introduced in v4.0.0) to work correctly +- **Breaking Change!** `Transform.modifyOutputStream(ZipOutputStream os)` to + `Transform.modifyOutputStream(ZipOutputStream jos, boolean preserveFileTimestamps)`. + Typically breaking changes are reserved for major version releases, but this change was necessary for + `preserverFileTimestamps` (introduced in v4.0.0) to work correctly in the presence of transformers, [#404](https://github.com/GradleUp/shadow/issues/404) - Fix regression in support Java 10+ during relocation, [#403](https://github.com/GradleUp/shadow/issues/403) ## [4.0.0](https://github.com/GradleUp/shadow/releases/tag/4.0.0) - 2018-09-25 -- **Breaking Change!** Restrict Plugin to Gradle 4.0+. Shadow major versions will align with Gradle major versions going forward. -- **Breaking Change!** For clarity purposes `com.github.johnrengelman.plugin-shadow` has been removed. If you intend to use this feature, you will need to declare your own `ConfigureShadowRelocation` task. See section [2.9.2](https://gradleup.com/shadow/#automatically_relocating_dependencies) of the User Guide +- **Breaking Change!** Restrict Plugin to Gradle 4.0+. Shadow major versions will align with Gradle major versions going + forward. +- **Breaking Change!** For clarity purposes `com.github.johnrengelman.plugin-shadow` has been removed. If you intend to + use this feature, you will need to declare your own `ConfigureShadowRelocation` task. See + section [2.9.2](https://gradleup.com/shadow/#automatically_relocating_dependencies) of the User Guide - [Sergey Tselovalnikov](https://github.com/SerCeMan) - Upgrade to ASM 6.2.1 to support Java 11 -- [Chris Cowan](https://github.com/Macil) - Add support for `shadowJar.preserveFileTimestamps` property. See [Jar.preserveFileTimestamps](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:preserveFileTimestamps) -- [Paul N. Baker](https://github.com/niko-dunixi) - Add `Log4j2PluginsCacheFileTransformer` to process Log4j DAT files during merge. +- [Chris Cowan](https://github.com/Macil) - Add support for `shadowJar.preserveFileTimestamps` property. + See [Jar.preserveFileTimestamps](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:preserveFileTimestamps) +- [Paul N. Baker](https://github.com/niko-dunixi) - Add `Log4j2PluginsCacheFileTransformer` to process Log4j DAT files + during merge. - [Felipe Lima](https://github.com/felipecsl) - Fix the long standing "No property `mainClassName`" issue. -- [debanne](https://github.com/debanne) - Implement JAR minimization actions. This will attempt to exclude unused classes in your shadowed JAR. -- Configure exclusion of `module-info.class` from `shadowJar` when using the Shadow the Java plugin, [#352](https://github.com/GradleUp/shadow/issues/352) +- [debanne](https://github.com/debanne) - Implement JAR minimization actions. This will attempt to exclude unused + classes in your shadowed JAR. +- Configure exclusion of `module-info.class` from `shadowJar` when using the Shadow the Java + plugin, [#352](https://github.com/GradleUp/shadow/issues/352) ## [2.0.4](https://github.com/GradleUp/shadow/releases/tag/2.0.4) - 2018-04-27 @@ -517,14 +639,17 @@ Instead, use the `enableRelocation = true` and `relocationPrefix = " Note the literal use of [`project.configurations`][Project.configurations] when setting the -[`configurations`][ShadowJar.configurations] attribute of a [`ShadowJar`][ShadowJar] task. -This is **required**. It may be tempting to specify `configurations = [configurations.compileClasspath]` but this will not -have the intended effect, as `configurations.compile` will try to delegate to the -[`configurations`][ShadowJar.configurations] property of the [`ShadowJar`][ShadowJar] task instead of the `project` +> [`configurations`][ShadowJar.configurations] attribute of a [`ShadowJar`][ShadowJar] task. +> This is **required**. It may be tempting to specify `configurations = [configurations.compileClasspath]` but this will +> not have the intended effect, as `configurations.compile` will try to delegate to the +> [`configurations`][ShadowJar.configurations] property of the [`ShadowJar`][ShadowJar] task instead of the `project` ## Embedding Jar Files Inside Your Shadow Jar @@ -134,8 +136,8 @@ Gradle's [`project.configurations`][Project.configurations] block. ``` > While not being able to filter entire transitive dependency graphs might seem like an oversight, it is necessary -because it would not be possible to intelligently determine the build author's intended results when there is a -common dependency between two 1st level dependencies when one is excluded and the other is not. +> because it would not be possible to intelligently determine the build author's intended results when there is a +> common dependency between two 1st level dependencies when one is excluded and the other is not. ### Using Regex Patterns to Filter Dependencies diff --git a/docs/configuration/filtering/README.md b/docs/configuration/filtering/README.md index f85dd1537..e60e10937 100644 --- a/docs/configuration/filtering/README.md +++ b/docs/configuration/filtering/README.md @@ -23,9 +23,8 @@ source set or _any_ of the dependencies to be merged. } ``` - -Excludes and includes can be combined just like a normal [`Jar`][Jar] task, with `excludes` taking precedence over `includes`. -Additionally, ANT style patterns can be used to match multiple files. +Excludes and includes can be combined just like a normal [`Jar`][Jar] task, with `excludes` taking precedence over +`includes`. Additionally, ANT style patterns can be used to match multiple files. === "Kotlin" diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 1882e310a..412b350cb 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -46,7 +46,8 @@ should process a particular entry and apply any modifications before writing the } ``` -Additionally, a [`ResourceTransformer`][ResourceTransformer] can accept a `Closure` to configure the provided `ResourceTransformer`. +Additionally, a [`ResourceTransformer`][ResourceTransformer] can accept a `Closure` to configure the provided +[`ResourceTransformer`][ResourceTransformer]. === "Kotlin" @@ -320,7 +321,6 @@ this transformer. } ``` - ## Appending XML Files XML files require a special transformer for merging. The [`XmlAppendingTransformer`][XmlAppendingTransformer] @@ -357,11 +357,12 @@ It must be added using the [`transform`][ShadowJar.transform] methods. - `FAIL`: Throw a `DuplicateFileCopyingException` when subsequent items are to be created at the same path. - `INCLUDE`: Do not attempt to prevent duplicates. - `INHERIT`: Uses the same strategy as the parent copy specification. -- `WARN`: Do not attempt to prevent duplicates, but log a warning message when multiple items are to be created at the same path. +- `WARN`: Do not attempt to prevent duplicates, but log a warning message when multiple items are to be created at the + same path. see more details about them in [`DuplicatesStrategy`][DuplicatesStrategy]. -`ShadowJar` recognizes `DuplicatesStrategy.INCLUDE` as the default, if you want to change the strategy, you can +`ShadowJar` recognizes `DuplicatesStrategy.INCLUDE` as the default, if you want to change the strategy, you can override it like: === "Kotlin" @@ -385,7 +386,8 @@ Different strategies will lead to different results for `foo/bar` files in the J - `EXCLUDE`: The **first** `foo/bar` file will be included in the final JAR. - `FAIL`: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicated `foo/bar` files. - `INCLUDE`: The **last** `foo/bar` file will be included in the final JAR (the default behavior). -- `INHERIT`: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. +- `INHERIT`: **Fail** the build with an exception like + `Entry .* is a duplicate but no duplicate handling strategy has been set`. - `WARN`: The **last** `foo/bar` file will be included in the final JAR, and a warning message will be logged. **NOTE:** The `duplicatesStrategy` takes precedence over transforming and relocating. If you mix the usages of @@ -412,7 +414,7 @@ Different strategies will lead to different results for `foo/bar` files in the J The [`ServiceFileTransformer`][ServiceFileTransformer] will not work as expected because the `duplicatesStrategy` will exclude the duplicated service files beforehand. However, this behavior might be what you expected for duplicated `foo/bar` files, preventing them from being included. -Want `ResourceTransformer`s and `duplicatesStrategy` to work together? There is a way to achieve this, leave the +Want `ResourceTransformer`s and `duplicatesStrategy` to work together? There is a way to achieve this, leave the `duplicatesStrategy` as `INCLUDE` and declare a custom [`ResourceTransformer`][ResourceTransformer] to handle the duplicated files. diff --git a/docs/configuration/minimizing/README.md b/docs/configuration/minimizing/README.md index 8eafc2321..897b815c8 100644 --- a/docs/configuration/minimizing/README.md +++ b/docs/configuration/minimizing/README.md @@ -1,6 +1,7 @@ # Minimizing -Shadow can automatically remove all classes of dependencies that are not used by the project, thereby minimizing the resulting shadowed JAR. +Shadow can automatically remove all classes of dependencies that are not used by the project, thereby minimizing the +resulting shadowed JAR. === "Kotlin" @@ -43,7 +44,8 @@ a `dependency` is interpreted as a regular expression. } ``` -> Dependencies scoped as `api` will be automatically excluded from minimization and used as "entry points" on minimization. +> Dependencies scoped as `api` will be automatically excluded from minimization and used as "entry points" on +> minimization. Similar to dependencies, projects can also be excluded. @@ -68,4 +70,4 @@ Similar to dependencies, projects can also be excluded. ``` > When excluding a `project`, all dependencies of the excluded `project` are automatically - excluded as well. +> excluded as well. diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 8dee715e2..3cfe0e8d0 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -32,17 +32,16 @@ In the resulting JAR, the class file is relocated from `junit/framework/TestCase `shadow/junit/TestCase.class`. > Relocation operates at a package level. -It is not necessary to specify any patterns for matching, it will operate simply on the prefix -provided. +> It is not necessary to specify any patterns for matching, it will operate simply on the prefix provided. > Relocation will be applied globally to all instances of the matched prefix. -That is, it does **not** scope to _only_ the dependencies being shadowed. -Be specific as possible when configuring relocation as to avoid unintended relocations. +> That is, it does **not** scope to _only_ the dependencies being shadowed. +> Be specific as possible when configuring relocation as to avoid unintended relocations. ## Filtering Relocation Specific classes or files can be `included`/`excluded` from the relocation operation if necessary. Use -[Ant Path Matcher](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html) +[Ant Path Matcher](https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/util/AntPathMatcher.html) syntax to specify matching path for your files and directories. === "Kotlin" @@ -71,8 +70,8 @@ syntax to specify matching path for your files and directories. } ``` -For a more advanced path matching you might want to use [Regular Expressions](https://regexr.com/) instead. Wrap the expression in `%regex[]` before -passing it to `include`/`exclude`. +For a more advanced path matching you might want to use [Regular Expressions](https://regexr.com/) instead. Wrap the +expression in `%regex[]` before passing it to `include`/`exclude`. === "Kotlin" @@ -96,8 +95,8 @@ passing it to `include`/`exclude`. ## Automatically Relocating Dependencies -Shadow is shipped with a task that can be used to automatically configure all packages from all dependencies to be relocated. -This feature was formally shipped into a 2nd plugin (`com.github.johnrengelman.plugin-shadow`) but has been +Shadow is shipped with a task that can be used to automatically configure all packages from all dependencies to be +relocated. This feature was formally shipped into a 2nd plugin (`com.github.johnrengelman.plugin-shadow`) but has been removed for clarity reasons in version 4.0.0. To configure automatic dependency relocation, set `enableRelocation = true` and optionally specify a custom @@ -124,13 +123,13 @@ To configure automatic dependency relocation, set `enableRelocation = true` and In versions before 8.1.0 it was necessary to configure a separate `ConfigureShadowRelocation` task for this. > Configuring package auto relocation can add significant time to the shadow process as it will process all dependencies -in the configurations declared to be shadowed. By default, this is the `runtime` or `runtimeClasspath` configurations. -Be mindful that some Gradle plugins will automatically add dependencies to your class path. You may need to remove these -dependencies if you do not intend to shadow them into your library. +> in the configurations declared to be shadowed. By default, this is the `runtime` or `runtimeClasspath` configurations. +> Be mindful that some Gradle plugins will automatically add dependencies to your class path. You may need to remove these +> dependencies if you do not intend to shadow them into your library. ## Relocating Project Resources Only -If you want to relocate the resources of the project only and exclude all dependencies (related to a normal JAR but with +If you want to relocate the resources of the project only and exclude all dependencies (related to a normal JAR but with relocating), you can try out the trick like: === "Kotlin" @@ -153,5 +152,5 @@ relocating), you can try out the trick like: } ``` -This is useful in some cases like [#759](https://github.com/GradleUp/shadow/issues/759) mentioned. See +This is useful in some cases like [#759](https://github.com/GradleUp/shadow/issues/759) mentioned. See [Configuring Shadowed Dependencies](../dependencies/README.md) for more information about `configurations`. diff --git a/docs/configuration/reproducible-builds/README.md b/docs/configuration/reproducible-builds/README.md index 9beaa5894..6b4b1b609 100644 --- a/docs/configuration/reproducible-builds/README.md +++ b/docs/configuration/reproducible-builds/README.md @@ -1,6 +1,9 @@ # Reproducible Builds -By default, JAR files generated by Gradle (with or without Shadow) for a single project with the same source code may not be identical to each other. Sometimes it's desirable to configure a project to consistently output a byte-for-byte identical JAR on every build. Gradle supports this with the following configuration, and Shadow will correctly respect these settings too: +By default, JAR files generated by Gradle (with or without Shadow) for a single project with the same source code may +not be identical to each other. Sometimes it's desirable to configure a project to consistently output a byte-for-byte +identical JAR on every build. Gradle supports this with the following configuration, and Shadow will correctly respect +these settings too: === "Kotlin" @@ -20,6 +23,10 @@ By default, JAR files generated by Gradle (with or without Shadow) for a single } ``` -One effect that this configuration will have is that the timestamps of all files in the JAR will be reset to a single consistent value. If your code or any files being included into the JAR depend on the timestamps being set accurately within the JAR, then this may not be the correct choice for you. +One effect that this configuration will have is that the timestamps of all files in the JAR will be reset to a single +consistent value. If your code or any files being included into the JAR depend on the timestamps being set accurately +within the JAR, then this may not be the correct choice for you. -See the [Reproducible archives section in Gradle's documentation](https://docs.gradle.org/4.9/userguide/working_with_files.html#sec:reproducible_archives) for more information. +See +the [Reproducible archives section in Gradle's documentation](https://docs.gradle.org/4.9/userguide/working_with_files.html#sec:reproducible_archives) +for more information. diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index 782d825cd..e861efc35 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -53,10 +53,9 @@ the output. } ``` -The code snippet above will generate a shadowed JAR containing both the `main` and `test` sources as well as all `testRuntimeOnly` -and `testImplementation` dependencies. -The file is output to `build/libs/--tests.jar`. - +The code snippet above will generate a shadowed JAR containing both the `main` and `test` sources as well as all +`testRuntimeOnly` and `testImplementation` dependencies. The file is output to +`build/libs/--tests.jar`. [Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 6b6e8b2ec..d46311f61 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -108,7 +108,8 @@ Alternatively, the plugin can be added to the buildscript classpath and applied:

-**NOTE:** The correct maven coordinates for each version of Shadow can be found by referencing the Gradle Plugin documentation [here](https://plugins.gradle.org/plugin/com.gradleup.shadow). +**NOTE:** The correct maven coordinates for each version of Shadow can be found by referencing the Gradle Plugin +documentation [here](https://plugins.gradle.org/plugin/com.gradleup.shadow). Shadow is a reactive plugin. This means that applying Shadow by itself will perform no configuration on your project. @@ -131,18 +132,20 @@ in their build logic), Shadow will automatically configure the following behavio * Configures the [`ShadowJar`][ShadowJar] task to generate a `Manifest` with: * Inheriting all configuration from the standard [`Jar`][Jar] task. * Adds a `Class-Path` attribute to the `Manifest` that appends all dependencies from the `shadow` configuration -* Configures the [`ShadowJar`][ShadowJar] task to _exclude_ any JAR index or cryptographic signature files matching the following patterns: +* Configures the [`ShadowJar`][ShadowJar] task to _exclude_ any JAR index or cryptographic signature files matching the + following patterns: * `META-INF/INDEX.LIST` * `META-INF/*.SF` * `META-INF/*.DSA` * `META-INF/*.RSA` * `META-INF/versions/**/module-info.class` * `module-info.class` -* Creates and registers the `shadow` component in the project (used for integrating with [`maven-publish`][maven-publish]). +* Creates and registers the `shadow` component in the project (used for integrating with + [`maven-publish`][maven-publish]). ## ShadowJar Command Line options -Sometimes, a user wants to declare the value of an exposed task property on the command line instead of the +Sometimes, a user wants to declare the value of an exposed task property on the command line instead of the build script. Passing property values on the command line is particularly helpful if they change more frequently. Here are the options that can be passed to the `shadowJar`: @@ -161,7 +164,8 @@ Also, you can view more information about the [`ShadowJar`][ShadowJar] task by r ./gradlew -q help --task shadowJar ``` -Refer to [listing command line options](https://docs.gradle.org/current/userguide/custom_tasks.html#sec:listing_task_options). +Refer to +[listing command line options](https://docs.gradle.org/current/userguide/custom_tasks.html#sec:listing_task_options). diff --git a/docs/gradle-plugins/README.md b/docs/gradle-plugins/README.md index 32798fea8..c12b06a33 100644 --- a/docs/gradle-plugins/README.md +++ b/docs/gradle-plugins/README.md @@ -5,10 +5,11 @@ conflicts with the same dependency provided by the Gradle runtime. If this is th to relocate your dependencies to a different package name to avoid the collision. Configuring the relocation has always been possible, but the build author is required to know all the package names -beforehand. As of Shadow v8.1.0, automatic package relocation can be enabled by setting the `enabledRelocation` +beforehand. As of Shadow v8.1.0, automatic package relocation can be enabled by setting the `enabledRelocation` and `relocationPrefix` settings on any [`ShadowJar`][ShadowJar] task. -A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the [`ShadowJar`][ShadowJar] task for relocation. +A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the [`ShadowJar`][ShadowJar] +task for relocation. === "Kotlin" @@ -57,15 +58,19 @@ A simple Gradle plugin can use this feature by applying the `shadow` plugin and ``` ## Publishing shadowed Gradle plugins + The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. Starting with this version, plugin projects that apply both Shadow and the Gradle Plugin Publish plugin will be -automatically configured to publish the output of the [`ShadowJar`][ShadowJar] tasks as the consumable artifact for the plugin. -See the [Gradle Plugin Publish docs](https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#shadow_dependencies) for details. +automatically configured to publish the output of the [`ShadowJar`][ShadowJar] tasks as the consumable artifact for the +plugin. See the +[Gradle Plugin Publish docs](https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#shadow_dependencies) +for details. ## Automatic package relocation with Shadow prior to v8.1.0 Prior to Shadow v8.1.0, Shadow handled this by introducing a new task type `ConfigureShadowRelocation`. -Tasks of this type are configured to target an instance of a [`ShadowJar`][ShadowJar] task and run immediately before it. +Tasks of this type are configured to target an instance of a [`ShadowJar`][ShadowJar] task and run immediately before +it. The `ConfigureShadowRelocation` task, scans the dependencies from the configurations specified on the associated `ShadowJar` task and collects the package names contained within them. It then configures relocation for these diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index aae18c2f4..88c8ac902 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -1,6 +1,6 @@ # Integrating with Kotlin Plugins -Kotlin standard libraries (stdlib) are added by Kotlin plugins by default via `implementation` (`runtimeClasspath`), +Kotlin standard libraries (stdlib) are added by Kotlin plugins by default via `implementation` (`runtimeClasspath`), they will be bundled into the shadowed JARs automatically. If you don't need a standard library at all, you can add the following Gradle property to your gradle.properties file: @@ -8,7 +8,7 @@ If you don't need a standard library at all, you can add the following Gradle pr kotlin.stdlib.default.dependency=false ``` -Kotlin compilations may still require the standard libraries, you can add them into `compileOnly` (`compileClasspath`) +Kotlin compilations may still require the standard libraries, you can add them into `compileOnly` (`compileClasspath`) to make sure compilations success and avoid shadowing as follows: === "Kotlin" @@ -27,7 +27,8 @@ to make sure compilations success and avoid shadowing as follows: } ``` -See more information about [Dependency on the standard library](https://kotlinlang.org/docs/gradle-configure-project.html#dependency-on-the-standard-library). +See more information about +[Dependency on the standard library](https://kotlinlang.org/docs/gradle-configure-project.html#dependency-on-the-standard-library). ## For Kotlin JVM Plugin @@ -60,7 +61,8 @@ Shadow works well for Kotlin JVM projects like Java projects. Here is an example ``` You can mix the Kotlin JVM plugin with `java-gradle-plugin`, `application`, and other Java plugins, -easily organize your build logic for [Packaging Gradle Plugins](../gradle-plugins/README.md), [Publishing Libraries](../publishing/README.md), +easily organize your build logic for +[Packaging Gradle Plugins](../gradle-plugins/README.md), [Publishing Libraries](../publishing/README.md), [Running Applications](../application-plugin/README.md), and so on. ## For Kotlin Multiplatform Plugin diff --git a/docs/publishing/README.md b/docs/publishing/README.md index f264b4a17..c7708c917 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -67,10 +67,9 @@ This automatic configuration occurs _only_ when using the above methods for configuring publishing. If this behavior is not desirable, then publishing **must** be manually configured. - ## Publish the Shadowed JAR instead of the Original JAR -You may want to publish the shadowed JAR instead of the original JAR. This can be done by trimming +You may want to publish the shadowed JAR instead of the original JAR. This can be done by trimming the `archiveClassifier` of the shadowed JAR like the following: === "Kotlin" @@ -148,11 +147,10 @@ the `archiveClassifier` of the shadowed JAR like the following: } ``` - ## Publish Custom ShadowJar Task Outputs It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via the -[`MavenPublication.artifact()`][MavenPublication.artifact] method. +[`MavenPublication.artifact()`][MavenPublication.artifact] method. === "Kotlin" @@ -220,10 +218,9 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th } ``` - ## Publish the Shadowed JAR with Custom Artifact Name -It is possible to configure the artifact name of the shadowed JAR via properties like `archiveBaseName`, see more +It is possible to configure the artifact name of the shadowed JAR via properties like `archiveBaseName`, see more customizable properties listed in [Configuring Output Name](../configuration/README.md#configuring-output-name). e.g. === "Kotlin" @@ -290,7 +287,7 @@ customizable properties listed in [Configuring Output Name](../configuration/REA } ``` -We modified `archiveClassifier`, `archiveExtension` and `archiveBaseName` in this example, the published artifact will +We modified `archiveClassifier`, `archiveExtension` and `archiveBaseName` in this example, the published artifact will be named `my-artifact-2.0-my-classifier.my-ext` instead of `1.0-all.jar`. From 2f79532e05679b5d89e42dcc05728994bd727139 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 16 Apr 2025 12:19:59 +0800 Subject: [PATCH 425/941] Update the name of changelog --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ea997ca1b..6b2033e91 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. ## Documentation - [User Guide](https://gradleup.com/shadow/) -- [CHANGELOG](docs/changes/README.md) +- [Change Log](docs/changes/README.md) ## Current Status From 0268dc10fdcd45fa89758561b7b53c14d204e130 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 17 Apr 2025 14:20:29 +0800 Subject: [PATCH 426/941] Miscs - Move lint-baseline file - Check docs dir existing - Optimize download dest dir check - Reformat --- build.gradle.kts | 14 ++++++++------ lint-baseline.xml => gradle/lint-baseline.xml | 0 2 files changed, 8 insertions(+), 6 deletions(-) rename lint-baseline.xml => gradle/lint-baseline.xml (100%) diff --git a/build.gradle.kts b/build.gradle.kts index b4c3f8e2e..0257832df 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -45,7 +45,7 @@ kotlin { } lint { - baseline = file("lint-baseline.xml") + baseline = file("gradle/lint-baseline.xml") ignoreTestSources = true warningsAsErrors = true } @@ -120,7 +120,9 @@ testing.suites { register("documentTest") { targets.configureEach { testTask { - val docsDir = file("docs") + val docsDir = file("docs").also { + if (!it.exists() || !it.isDirectory) error("Docs dir $it does not exist or is not a directory.") + } // Add docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. inputs.dir(docsDir) systemProperty("DOCS_DIR", docsDir.absolutePath) @@ -216,16 +218,16 @@ tasks.check { tasks.register("downloadStartScripts") { description = "Download start scripts from Gradle sources, this should be run intervally to track updates." - val urlPrefix = "https://raw.githubusercontent.com/gradle/gradle/refs/heads/master/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins" + val urlPrefix = "https://raw.githubusercontent.com/gradle/gradle/refs/heads/master/platforms/jvm/" + + "plugins-application/src/main/resources/org/gradle/api/internal/plugins" from(resources.text.fromUri("$urlPrefix/unixStartScript.txt")) { rename { "unixStartScript.txt" } } from(resources.text.fromUri("$urlPrefix/windowsStartScript.txt")) { rename { "windowsStartScript.txt" } } - val destDir = file("src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal") - if (!destDir.exists() || !destDir.isDirectory || destDir.listFiles().isNullOrEmpty()) { - error("Download destination dir $destDir does not exist or is empty.") + val destDir = file("src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal").also { + if (!it.exists() || !it.isDirectory) error("Download destination dir $it does not exist or is not a directory.") } into(destDir) } diff --git a/lint-baseline.xml b/gradle/lint-baseline.xml similarity index 100% rename from lint-baseline.xml rename to gradle/lint-baseline.xml From 785150ba72d1ea0697afe30a998cc5186962e22f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 17 Apr 2025 06:14:29 -0400 Subject: [PATCH 427/941] Test that string consts are relocated by default (#1405) * Test `relocateStringConstantsByDefault` * Fix the test * Cleanup --- .../gradle/plugins/shadow/RelocationTest.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index eb1543a82..083d955ce 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -8,9 +8,11 @@ import assertk.assertions.isInstanceOf import assertk.assertions.isNotEmpty import assertk.fail import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.Companion.CONSTANT_TIME_FOR_ZIP_ENTRIES import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsOnly +import com.github.jengelman.gradle.plugins.shadow.util.runProcess import java.net.URLClassLoader import kotlin.io.path.appendText import kotlin.io.path.writeText @@ -581,6 +583,37 @@ class RelocationTest : BasePluginTest() { } } + @Test + fun relocateStringConstantsByDefault() { + writeClass { + """ + package my; + public class Main { + public static void main(String[] args) { + System.out.println("junit.framework.Test"); + } + } + """.trimIndent() + } + projectScriptPath.appendText( + """ + $shadowJar { + manifest { + attributes '$mainClassAttributeKey': 'my.Main' + } + relocate('junit', 'foo.junit') + } + """.trimIndent(), + ) + + run(shadowJarTask) + val result = runProcess("java", "-jar", outputShadowJar.use { it.toString() }) + + assertThat(result).contains( + "foo.junit.framework.Test", + ) + } + private companion object { @JvmStatic fun prefixProvider() = listOf( From c86a9b33256ced4a9bd8500bb1053ac89a54ebcf Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 18 Apr 2025 11:06:50 +0800 Subject: [PATCH 428/941] Upload the artifacts --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 76fe640b0..e79413526 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,3 +42,7 @@ jobs: env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USER }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} + - uses: actions/upload-artifact@v4 + with: + path: build/libs + if-no-files-found: 'error' From 70866550f5886cab7a3eb2a8008c111b57afe9ac Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 20 Apr 2025 21:05:56 -0400 Subject: [PATCH 429/941] Reduce nesting for Plugin.apply (#1407) --- .../plugins/shadow/ShadowApplicationPlugin.kt | 12 +++--- .../gradle/plugins/shadow/ShadowBasePlugin.kt | 6 +-- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 10 ++--- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 10 ++--- .../gradle/plugins/shadow/ShadowPlugin.kt | 40 +++++++++---------- 5 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index fc0ba4c2f..30d9ebde3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -27,12 +27,12 @@ import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator * @see [ApplicationPlugin] */ public abstract class ShadowApplicationPlugin : Plugin { - override fun apply(project: Project) { - project.addRunTask() - project.addCreateScriptsTask() - project.configureDistribution() - project.configureShadowJarMainClass() - project.configureInstallTask() + override fun apply(project: Project): Unit = with(project) { + addRunTask() + addCreateScriptsTask() + configureDistribution() + configureShadowJarMainClass() + configureInstallTask() } protected open fun Project.addRunTask() { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 1539d6153..912f17874 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -10,13 +10,13 @@ import org.gradle.util.GradleVersion public abstract class ShadowBasePlugin : Plugin { - override fun apply(project: Project) { + override fun apply(project: Project): Unit = with(project) { if (GradleVersion.current() < GradleVersion.version("8.3")) { throw GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.") } @Suppress("DEPRECATION") - project.extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) - project.configurations.create(CONFIGURATION_NAME) + extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) + configurations.create(CONFIGURATION_NAME) } public companion object { 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 08a6351cc..33ca12340 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 @@ -31,11 +31,11 @@ public abstract class ShadowJavaPlugin @Inject constructor( private val softwareComponentFactory: SoftwareComponentFactory, ) : Plugin { - override fun apply(project: Project) { - project.configureShadowJar() - project.configureConfigurations() - project.configureComponents() - project.configureJavaGradlePlugin() + override fun apply(project: Project): Unit = with(project) { + configureShadowJar() + configureConfigurations() + configureComponents() + configureJavaGradlePlugin() } protected open fun Project.configureShadowJar() { 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 a876bf149..174d0e95c 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 @@ -12,13 +12,11 @@ import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget public abstract class ShadowKmpPlugin : Plugin { - override fun apply(project: Project) { - with(project) { - extensions.getByType(KotlinMultiplatformExtension::class.java).targets.configureEach { target -> - if (target !is KotlinJvmTarget) return@configureEach + override fun apply(project: Project): Unit = with(project) { + extensions.getByType(KotlinMultiplatformExtension::class.java).targets.configureEach { target -> + if (target !is KotlinJvmTarget) return@configureEach - configureShadowJar(target) - } + configureShadowJar(target) } } 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 7a57fbd42..df999e091 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 @@ -8,27 +8,25 @@ import org.gradle.api.plugins.JavaPlugin public abstract class ShadowPlugin : Plugin { - override fun apply(project: Project) { - with(project.plugins) { - apply(ShadowBasePlugin::class.java) - @Suppress("WithTypeWithoutConfigureEach") - withType(JavaPlugin::class.java) { - apply(ShadowJavaPlugin::class.java) - } - @Suppress("WithTypeWithoutConfigureEach") - withType(ApplicationPlugin::class.java) { - apply(ShadowApplicationPlugin::class.java) - } - withId("org.jetbrains.kotlin.multiplatform") { - apply(ShadowKmpPlugin::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. - // If the user applies shadow before those plugins. However, this is fine, because this was also - // the behavior with the old plugin when applying in that order. - apply(LegacyShadowPlugin::class.java) + override fun apply(project: Project): Unit = with(project.plugins) { + apply(ShadowBasePlugin::class.java) + @Suppress("WithTypeWithoutConfigureEach") + withType(JavaPlugin::class.java) { + apply(ShadowJavaPlugin::class.java) + } + @Suppress("WithTypeWithoutConfigureEach") + withType(ApplicationPlugin::class.java) { + apply(ShadowApplicationPlugin::class.java) } + withId("org.jetbrains.kotlin.multiplatform") { + apply(ShadowKmpPlugin::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. + // If the user applies shadow before those plugins. However, this is fine, because this was also + // the behavior with the old plugin when applying in that order. + apply(LegacyShadowPlugin::class.java) } } From 531b14ce2793e6c56ddac7fe5cce13d0aba41886 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 08:07:02 +0800 Subject: [PATCH 430/941] Update plugin android-lint to v8.9.2 (#1408) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 42c44e027..005b0e8f2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.9.1" +android-lint = "com.android.lint:8.9.2" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.31.0" From 1afb34c26640b0dba169248de7d65af2f3cd5071 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 22 Apr 2025 22:54:41 -0400 Subject: [PATCH 431/941] Polish KGP version checker (#1409) * Copy-pasted from https://github.com/vanniktech/gradle-maven-publish-plugin/blob/main/plugin/src/main/kotlin/com/vanniktech/maven/publish/MavenPublishBasePlugin.kt#L17-L41. * Test for KMP 2.0.21-RC * Rename * Simplify `isAtLeastKgpVersion` * Revert "Test for KMP 2.0.21-RC" This reverts commit 5eecf0f0ee6f99e6865e9921fb9cef6a7903e22d. * Revert "Simplify `isAtLeastKgpVersion`" This reverts commit 595414eb9566aa4819fb9a6ec873709b12a519de. --- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 4 ++-- .../gradle/plugins/shadow/ShadowPlugin.kt | 3 ++- .../plugins/shadow/internal/KotlinCompat.kt | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt 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 174d0e95c..4245ad93d 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,13 +1,13 @@ 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.isAtLeastKgpVersion import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey 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 -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion as KgpVersion import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget public abstract class ShadowKmpPlugin : Plugin { @@ -30,7 +30,7 @@ public abstract class ShadowKmpPlugin : Plugin { }, ) - if (KgpVersion.DEFAULT < KgpVersion.KOTLIN_2_1) return@registerShadowJarCommon + if (!isAtLeastKgpVersion(2, 1, 0)) return@registerShadowJarCommon @OptIn(ExperimentalKotlinGradlePluginApi::class) target.mainRun { 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 df999e091..b35c5dc27 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 @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.internal.KOTLIN_MULTIPLATFORM_PLUGIN_ID import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import org.gradle.api.Plugin import org.gradle.api.Project @@ -18,7 +19,7 @@ public abstract class ShadowPlugin : Plugin { withType(ApplicationPlugin::class.java) { apply(ShadowApplicationPlugin::class.java) } - withId("org.jetbrains.kotlin.multiplatform") { + withId(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { apply(ShadowKmpPlugin::class.java) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt new file mode 100644 index 000000000..b55a70516 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt @@ -0,0 +1,20 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin + +internal const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform" + +internal fun Project.isAtLeastKgpVersion( + major: Int, + minor: Int, + patch: Int, + id: String = KOTLIN_MULTIPLATFORM_PLUGIN_ID, +): Boolean { + val plugin = plugins.getPlugin(id) as KotlinBasePlugin + val elements = plugin.pluginVersion.takeWhile { it != '-' }.split(".") + val kgpMajor = elements[0].toInt() + val kgpMinor = elements[1].toInt() + val kgpPatch = elements[2].toInt() + return kgpMajor > major || (kgpMajor == major && (kgpMinor > minor || (kgpMinor == minor && kgpPatch >= patch))) +} From b6143ef964be0419c0dbca9e3e4c7da99e64840f Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 23 Apr 2025 13:23:05 +0800 Subject: [PATCH 432/941] Fix the link for @niko-dunixi --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 6e4ddbc35..6b6ca7ffc 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -621,7 +621,7 @@ type. - [Sergey Tselovalnikov](https://github.com/SerCeMan) - Upgrade to ASM 6.2.1 to support Java 11 - [Chris Cowan](https://github.com/Macil) - Add support for `shadowJar.preserveFileTimestamps` property. See [Jar.preserveFileTimestamps](https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html#org.gradle.api.tasks.bundling.Jar:preserveFileTimestamps) -- [Paul N. Baker](https://github.com/niko-dunixi) - Add `Log4j2PluginsCacheFileTransformer` to process Log4j DAT files +- [Paul N. Baker](https://github.com/nikole-dunixi) - Add `Log4j2PluginsCacheFileTransformer` to process Log4j DAT files during merge. - [Felipe Lima](https://github.com/felipecsl) - Fix the long standing "No property `mainClassName`" issue. - [debanne](https://github.com/debanne) - Implement JAR minimization actions. This will attempt to exclude unused From afbae305633eb9b568df73ed0a54e16ab6200c7b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 23 Apr 2025 02:11:41 -0400 Subject: [PATCH 433/941] Set Main-Class attr for KMP 1.9.0 or above (#1410) https://github.com/JetBrains/kotlin/releases/tag/v1.9.0 https://github.com/JetBrains/kotlin/commit/e0389a0a870766b3d595b93c24905b491d02fab9 https://youtrack.jetbrains.com/issue/KT-58661 --- docs/changes/README.md | 4 ++++ docs/kotlin-plugins/README.md | 4 ++-- .../github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 6b6ca7ffc..eef391d35 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,6 +2,10 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta12...HEAD) - 2025-xx-xx +**Changed** + +- Set `Main-Class` attr for KMP 1.9.0 or above. ([#1410](https://github.com/GradleUp/shadow/pull/1410)) + **Fixed** - Avoid creating jvm targets eagerly for KMP. ([#1378](https://github.com/GradleUp/shadow/pull/1378)) diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index 88c8ac902..ac5f4ff26 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -83,7 +83,7 @@ automatically configure additional tasks for bundling the shadowed JAR for its ` kotlin { @Suppress("OPT_IN_USAGE") jvm().mainRun { - // Optionally, set the main class for `runJvm`, it's available from Kotlin 2.1.0 + // Optionally, set the main class for `runJvm`. mainClass = "myapp.MainKt" } sourceSets { @@ -121,7 +121,7 @@ automatically configure additional tasks for bundling the shadowed JAR for its ` kotlin { jvm().mainRun { - // Optionally, set the main class for `runJvm`, it's available from Kotlin 2.1.0 + // Optionally, set the main class for `runJvm`. it.mainClass.set('myapp.MainKt') } sourceSets { 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 4245ad93d..6413e5f41 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 @@ -30,7 +30,7 @@ public abstract class ShadowKmpPlugin : Plugin { }, ) - if (!isAtLeastKgpVersion(2, 1, 0)) return@registerShadowJarCommon + if (!isAtLeastKgpVersion(1, 9, 0)) return@registerShadowJarCommon @OptIn(ExperimentalKotlinGradlePluginApi::class) target.mainRun { From 5e069ab2351ceb4ba0740e2a1d750312ae2583ab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 01:31:33 +0000 Subject: [PATCH 434/941] Update dependency androidx.lint:lint-gradle to v1.0.0-alpha04 (#1411) * Update dependency androidx.lint:lint-gradle to v1.0.0-alpha04 * Update baseline --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- gradle/libs.versions.toml | 2 +- gradle/lint-baseline.xml | 90 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 005b0e8f2..c5d40cb2d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.to kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } -androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha03" +androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha04" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index 1aeb46647..ad2f11d02 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 45254a3a0aa9b36b84053817f2f8200c34c74bc6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 09:06:36 +0800 Subject: [PATCH 435/941] Update dependency gradle to v8.14 (#1413) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 43583 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 6 +++--- gradlew.bat | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index a4b76b9530d66f5e68d973ea569d8e19de379189..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 34943 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

Gn4F+B~IivciU9^t0j-Mgao3 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 DDw4oP diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f853b1c..ca025c83a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index f3b75f3b0..23d15a936 100755 --- a/gradlew +++ b/gradlew @@ -114,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -205,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -213,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 9d21a2183..db3a6ac20 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,11 @@ goto fail :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell From dbf8acbd53a76d76d53dc8c27232d7c7b7868819 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 26 Apr 2025 09:07:45 +0800 Subject: [PATCH 436/941] Update plugin com.gradle.develocity to v4.0.1 (#1412) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 2cfa55c0e..4eca2b2cf 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "4.0" + id("com.gradle.develocity") version "4.0.1" } develocity { From bd52a19532787df2379d706e7629524a6440abe4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 27 Apr 2025 22:51:46 -0400 Subject: [PATCH 437/941] Migrate credentials for publishing to Maven Central (#1414) https://central.sonatype.org/news/20250326_ossrh_sunset/ * Update `ORG_GRADLE_PROJECT_mavenCentralUsername` and `ORG_GRADLE_PROJECT_mavenCentralPassword` * Remove limits and test the release * Update `SONATYPE_HOST` to `CENTRAL_PORTAL` * Revert "Remove limits and test the release" This reverts commit 0cc04fdb24afc98673f1a043b4c1d8e9ab7358f5. --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- gradle.properties | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e79413526..8a5e82d38 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,8 +40,8 @@ jobs: # TODO: https://github.com/gradle/gradle/issues/22779 - run: ./gradlew publishToMavenCentral --no-configuration-cache env: - ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USER }} - ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_PORTAL_USERNAME }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }} - uses: actions/upload-artifact@v4 with: path: build/libs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 245de4902..56532ea16 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -27,8 +27,8 @@ jobs: env: GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_KEY }} GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_SECRET }} - ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USER }} - ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_PORTAL_USERNAME }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} - name: Extract release notes diff --git a/gradle.properties b/gradle.properties index 54f32c831..dce25537f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ POM_ARTIFACT_ID=shadow-gradle-plugin VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true -SONATYPE_HOST=DEFAULT +SONATYPE_HOST=CENTRAL_PORTAL RELEASE_SIGNING_ENABLED=true POM_NAME=Shadow Gradle Plugin From f19ed163cd834ed97ae6b74a10694fb316addd35 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 28 Apr 2025 15:47:24 +0800 Subject: [PATCH 438/941] Update and rename ci.yml to build.yml --- .github/workflows/{ci.yml => build.yml} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename .github/workflows/{ci.yml => build.yml} (99%) diff --git a/.github/workflows/ci.yml b/.github/workflows/build.yml similarity index 99% rename from .github/workflows/ci.yml rename to .github/workflows/build.yml index 8a5e82d38..a2b1280d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: CI +name: Build on: push: From cb289dac7b8048c862b43f53efde4537d963aab7 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 29 Apr 2025 10:46:59 +0800 Subject: [PATCH 439/941] Prepare version 9.0.0-beta13 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index eef391d35..21400b4c4 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta12...HEAD) - 2025-xx-xx +## [9.0.0-beta13](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta13) - 2025-04-29 **Changed** diff --git a/gradle.properties b/gradle.properties index dce25537f..a0d6c2344 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta13 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From 804dc9ab3cc3797613968957f9d6a37bd27763b8 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 29 Apr 2025 10:48:05 +0800 Subject: [PATCH 440/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 21400b4c4..46034821e 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,5 +1,8 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta13...HEAD) - 2025-xx-xx + + ## [9.0.0-beta13](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta13) - 2025-04-29 **Changed** diff --git a/gradle.properties b/gradle.properties index a0d6c2344..dce25537f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta13 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From 7fed0895f61b0596d623e6ce91e6396fd4322d83 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 29 Apr 2025 10:54:09 +0800 Subject: [PATCH 441/941] Fix CI badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b2033e91..e9e846d88 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. [![Maven Central](https://img.shields.io/maven-central/v/com.gradleup.shadow/shadow-gradle-plugin)](https://central.sonatype.com/artifact/com.gradleup.shadow/shadow-gradle-plugin) [![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/com.gradleup.shadow/shadow-gradle-plugin?&server=https://oss.sonatype.org/)](https://oss.sonatype.org/content/repositories/snapshots/com/gradleup/shadow/) [![Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/com.gradleup.shadow)](https://plugins.gradle.org/plugin/com.gradleup.shadow) -[![CI](https://github.com/GradleUp/shadow/actions/workflows/ci.yml/badge.svg?branch=main&event=push)](https://github.com/GradleUp/shadow/actions/workflows/ci.yml?query=branch:main+event:push) +[![CI](https://github.com/GradleUp/shadow/actions/workflows/build.yml/badge.svg?branch=main&event=push)](https://github.com/GradleUp/shadow/actions/workflows/build.yml?query=branch:main+event:push) [![License](https://img.shields.io/github/license/GradleUp/shadow.svg)](LICENSE) ## Compatibility Matrix From f67905a1ad27b450bace7eab5ca7f59b31c32731 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 30 Apr 2025 04:57:25 -0400 Subject: [PATCH 442/941] Update start scripts automatically (#1418) Refs https://github.com/mdogan/homebrew-zulu/blob/7a84f082eea304d4f6fdc27e77389742af940622/.github/workflows/upgrade.yml --- .github/workflows/start-scripts.yml | 34 +++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .github/workflows/start-scripts.yml diff --git a/.github/workflows/start-scripts.yml b/.github/workflows/start-scripts.yml new file mode 100644 index 000000000..17112ed6d --- /dev/null +++ b/.github/workflows/start-scripts.yml @@ -0,0 +1,34 @@ +name: Update Start Scripts + +on: + push: + branches: + - main + schedule: + - cron: '0 */3 * * *' + workflow_dispatch: + +jobs: + update: + runs-on: ubuntu-latest + if: github.event.repository.fork == false + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + - uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: true + - run: ./gradlew downloadStartScripts + - uses: peter-evans/create-pull-request@v7 + with: + commit-message: | + Update Start Scripts to the latest + + _Auto-generated by `Update Start Scripts` Github workflow._ + title: Update Start Scripts to the latest + body: _Auto-generated by `Update Start Scripts` Github workflow._ + branch: actions/update-start-scripts + delete-branch: true From fe4ab452721b99b8a0b05aea3bab6dca9bc4033f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 17:11:20 +0800 Subject: [PATCH 443/941] Update start script templates to the latest (#1419) * Update Start Scripts to the latest _Auto-generated by `Update Start Scripts` Github workflow._ * Update changelog * Update the PR name --------- Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> Co-authored-by: Goooler --- .github/workflows/start-scripts.yml | 4 ++-- docs/changes/README.md | 3 +++ .../gradle/plugins/shadow/internal/unixStartScript.txt | 6 +++--- .../gradle/plugins/shadow/internal/windowsStartScript.txt | 2 +- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/start-scripts.yml b/.github/workflows/start-scripts.yml index 17112ed6d..d8267adb2 100644 --- a/.github/workflows/start-scripts.yml +++ b/.github/workflows/start-scripts.yml @@ -25,10 +25,10 @@ jobs: - uses: peter-evans/create-pull-request@v7 with: commit-message: | - Update Start Scripts to the latest + Update start script templates to the latest _Auto-generated by `Update Start Scripts` Github workflow._ - title: Update Start Scripts to the latest + title: Update start script templates to the latest body: _Auto-generated by `Update Start Scripts` Github workflow._ branch: actions/update-start-scripts delete-branch: true diff --git a/docs/changes/README.md b/docs/changes/README.md index 46034821e..e1488af62 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,6 +2,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta13...HEAD) - 2025-xx-xx +**Changed** + +- Update start script templates. ([#1419](https://github.com/GradleUp/shadow/pull/1419)) ## [9.0.0-beta13](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta13) - 2025-04-29 diff --git a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt index f082fb120..c71b056fc 100644 --- a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt +++ b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -243,7 +243,7 @@ fi DEFAULT_JVM_OPTS=${defaultJvmOpts} # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect \${Hostname} to be expanded, as it is an environment variable and will be # treated as '\${Hostname}' itself on the command line. @@ -254,7 +254,7 @@ set -- \\ <% } %> -classpath "\$CLASSPATH" \\ <% if ( mainClassName.startsWith('--module ') ) { %> --module-path "\$MODULE_PATH" \\ -<% } %> ${mainClassName} \\ +<% } %> ${mainClassName ?: entryPointArgs} \\ "\$@" # Stop when "xargs" is not available. diff --git a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt index b487fee0e..95b88fe56 100644 --- a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt +++ b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt @@ -75,7 +75,7 @@ set CLASSPATH=$classpath <% if ( mainClassName.startsWith('--module ') ) { %>set MODULE_PATH=$modulePath<% } %> @rem Execute ${applicationName} -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %> -classpath "%CLASSPATH%" <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "%MODULE_PATH%" <% } %>${mainClassName} %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %> -classpath "%CLASSPATH%" <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "%MODULE_PATH%" <% } %>${mainClassName ?: entryPointArgs} %* :end @rem End local scope for the variables with windows NT shell From 9577baf3cc23a65fa2e64b43dbd2fc1a8c1b7d57 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 30 Apr 2025 05:48:37 -0400 Subject: [PATCH 444/941] Bump Kotlin to 2.2.0-Beta1 for Java 24 support (#1420) --- build.gradle.kts | 1 + gradle/libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0257832df..896dc162c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,6 +36,7 @@ kotlin { explicitApi() compilerOptions { // https://docs.gradle.org/current/userguide/compatibility.html#kotlin + @Suppress("DEPRECATION") // TODO: bump apiVersion to 2.0 to match Gradle 9.0 apiVersion = KotlinVersion.KOTLIN_1_8 jvmTarget = JvmTarget.JVM_11 freeCompilerArgs.addAll( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c5d40cb2d..e1a8a6deb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.1.20" +kotlin = "2.2.0-Beta1" moshi = "1.15.2" pluginPublish = "1.3.1" From c4b0d456a9e82c76d9a2045770a43cdd8a1f2d8d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 30 Apr 2025 05:58:30 -0400 Subject: [PATCH 445/941] Add Java 24 into test matrix (#1356) --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a2b1280d3..69b465dfb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: matrix: os: [ ubuntu-latest, windows-latest ] # Always test on the latest version and some LTS. - java: [ 17, 21, 23 ] + java: [ 17, 21, 24 ] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 From 5e44c138592c37b5b01a3bbd23cc7ad468f5297e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 May 2025 09:40:35 +0800 Subject: [PATCH 446/941] Update kotlin monorepo to v2.2.0-Beta2 (#1421) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e1a8a6deb..d0616f056 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.2.0-Beta1" +kotlin = "2.2.0-Beta2" moshi = "1.15.2" pluginPublish = "1.3.1" From 187b27a1bc326d0e39c3af19cfd0028bd7b0be6e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 6 May 2025 05:16:47 -0400 Subject: [PATCH 447/941] Update the TODO in KMP example (#1423) --- docs/kotlin-plugins/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index ac5f4ff26..250e2c401 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -100,7 +100,7 @@ automatically configure additional tasks for bundling the shadowed JAR for its ` } } - // TODO: we can't use `tasks.shadowJar` here like the other examples, something wrong with Gradle or Kotlin plugin? + // TODO: Gradle doesn't generate accessors for this use case, so we can't call `tasks.shadowJar` directly like the other examples. tasks.named("shadowJar") { manifest { // Optionally, set the main class for the shadowed JAR. From 9eaa1cb3787b0967d664ec1521d162d6f2f9bc88 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 May 2025 05:17:43 +0800 Subject: [PATCH 448/941] Update plugin android-lint to v8.10.0 (#1425) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d0616f056..4ae675a5e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.9.2" +android-lint = "com.android.lint:8.10.0" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.31.0" From ab1a9a04aa0e9fb2e08b7ff2524747186a1d4d00 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 15 May 2025 08:14:59 +0800 Subject: [PATCH 449/941] Update plugin mavenPublish to v0.32.0 (#1428) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4ae675a5e..a5cc216de 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,6 +34,6 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.10.0" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" -mavenPublish = "com.vanniktech.maven.publish:0.31.0" +mavenPublish = "com.vanniktech.maven.publish:0.32.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } spotless = "com.diffplug.spotless:7.0.3" From 31fbc0bf24459de6fda609a8b3c3767c6973bc80 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 15 May 2025 23:32:54 -0400 Subject: [PATCH 450/941] There's no need to run update-start-scripts job frequently (#1429) The start scripts are typically updated with Gradle releases. Refs https://github.com/Goooler/agp-sources/blob/ac23064e6529e504d1dbabbd2cccec3c60bc4c2c/.github/workflows/ci.yml#L22-L45. --- .github/workflows/build.yml | 23 +++++++++++++++++++ .github/workflows/start-scripts.yml | 34 ----------------------------- 2 files changed, 23 insertions(+), 34 deletions(-) delete mode 100644 .github/workflows/start-scripts.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 69b465dfb..cab16e3fb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -24,6 +24,29 @@ jobs: - uses: gradle/actions/setup-gradle@v4 - run: ./gradlew build + # There's no need to run the update task frequently; the start scripts are typically updated with Gradle releases. + update-start-scripts: + runs-on: ubuntu-latest + if: github.event.repository.fork == false && github.ref == 'refs/heads/main' + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + - uses: gradle/actions/setup-gradle@v4 + - run: ./gradlew downloadStartScripts + - uses: peter-evans/create-pull-request@v7 + with: + commit-message: | + Update start script templates to the latest + + _Auto-generated by `Update Start Scripts` Github workflow._ + title: Update start script templates to the latest + body: _Auto-generated by `Update Start Scripts` Github workflow._ + branch: actions/update-start-scripts + delete-branch: true + publish-snapshot: needs: build runs-on: ubuntu-latest diff --git a/.github/workflows/start-scripts.yml b/.github/workflows/start-scripts.yml deleted file mode 100644 index d8267adb2..000000000 --- a/.github/workflows/start-scripts.yml +++ /dev/null @@ -1,34 +0,0 @@ -name: Update Start Scripts - -on: - push: - branches: - - main - schedule: - - cron: '0 */3 * * *' - workflow_dispatch: - -jobs: - update: - runs-on: ubuntu-latest - if: github.event.repository.fork == false - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - distribution: 'zulu' - java-version: 21 - - uses: gradle/actions/setup-gradle@v4 - with: - cache-read-only: true - - run: ./gradlew downloadStartScripts - - uses: peter-evans/create-pull-request@v7 - with: - commit-message: | - Update start script templates to the latest - - _Auto-generated by `Update Start Scripts` Github workflow._ - title: Update start script templates to the latest - body: _Auto-generated by `Update Start Scripts` Github workflow._ - branch: actions/update-start-scripts - delete-branch: true From 55655b0baf0c8ee1e9a0be48d1ea18c56a38889e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 11:13:38 +0000 Subject: [PATCH 451/941] Update kotlin monorepo to v2.2.0-RC (#1430) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a5cc216de..2c4f04e4e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.2.0-Beta2" +kotlin = "2.2.0-RC" moshi = "1.15.2" pluginPublish = "1.3.1" From 1d7b0863fed3126bf376f11d563e9176de176cd3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 19 May 2025 08:53:38 +0000 Subject: [PATCH 452/941] Update dependency org.xmlunit:xmlunit-legacy to v2.10.1 (#1431) * Update dependency org.xmlunit:xmlunit-legacy to v2.10.1 * Add `org.xmlunit:xmlunit-core` back ```diff -\--- org.xmlunit:xmlunit-legacy:2.10.0 - +--- org.xmlunit:xmlunit-core:2.10.0 - \--- junit:junit:3.8.1 +\--- org.xmlunit:xmlunit-legacy:2.10.1 + \--- junit:junit:3.8.1 ``` --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- build.gradle.kts | 3 ++- gradle/libs.versions.toml | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 896dc162c..3656fdb41 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -115,7 +115,8 @@ dependencies { testing.suites { getByName("test") { dependencies { - implementation(libs.xmlunit) + implementation(libs.xmlunit.core) + implementation(libs.xmlunit.legacy) } } register("documentTest") { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2c4f04e4e..07e038ba3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,6 +2,7 @@ kotlin = "2.2.0-RC" moshi = "1.15.2" pluginPublish = "1.3.1" +xmlunit = "2.10.1" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" @@ -14,7 +15,8 @@ jdependency = "org.vafer:jdependency:2.12" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" -xmlunit = "org.xmlunit:xmlunit-legacy:2.10.0" +xmlunit-core = { module = "org.xmlunit:xmlunit-core", version.ref = "xmlunit" } +xmlunit-legacy = { module = "org.xmlunit:xmlunit-legacy", version.ref = "xmlunit" } moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } From 6ea70068fa9c6fa86a0887329f834b8baebeea1f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 07:07:00 +0800 Subject: [PATCH 453/941] Update dependency com.pinterest.ktlint:ktlint-cli to v1.6.0 (#1432) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 07e038ba3..ed03c8440 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.r androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha04" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. -ktlint = "com.pinterest.ktlint:ktlint-cli:1.5.0" +ktlint = "com.pinterest.ktlint:ktlint-cli:1.6.0" junit-bom = "org.junit:junit-bom:5.12.2" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" From e174d36c7774cb8da93e008ed2fbd0cc0e4e86ed Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 09:30:16 +0800 Subject: [PATCH 454/941] Update dependency org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin to v1 (#1433) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ed03c8440..af7e69604 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,7 +20,7 @@ xmlunit-legacy = { module = "org.xmlunit:xmlunit-legacy", version.ref = "xmlunit 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.10.0" +foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:1.0.0" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } From 19b615191ab74fbb255fec7db5fff136af58f36c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 01:49:00 +0000 Subject: [PATCH 455/941] Update dependency androidx.lint:lint-gradle to v1.0.0-alpha05 (#1434) * Update dependency androidx.lint:lint-gradle to v1.0.0-alpha05 * Suppress EagerGradleConfiguration * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- .../github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt | 1 + .../github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt | 3 ++- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index af7e69604..159ea443a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.to kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } -androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha04" +androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.6.0" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 912f17874..77a6c09a7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -16,6 +16,7 @@ public abstract class ShadowBasePlugin : Plugin { } @Suppress("DEPRECATION") extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) + @Suppress("EagerGradleConfiguration") // this should be created eagerly. configurations.create(CONFIGURATION_NAME) } 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 33ca12340..5ac21fdfe 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 @@ -41,7 +41,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( protected open fun Project.configureShadowJar() { val jarTask = tasks.jar val taskProvider = registerShadowJarCommon { task -> - @Suppress("EagerGradleConfiguration") + @Suppress("EagerGradleConfiguration") // mergeSpec.from hasn't supported lazy configuration yet. task.manifest.inheritFrom(jarTask.get().manifest) val attrProvider = jarTask.map { it.manifest.attributes[classPathAttributeKey]?.toString().orEmpty() } val files = files(configurations.shadow) @@ -62,6 +62,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { it.extendsFrom(shadowConfiguration) } + @Suppress("EagerGradleConfiguration") // this should be created eagerly. configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { it.extendsFrom(shadowConfiguration) it.isCanBeConsumed = true From 01fff8afa9b01354f5743b995241dc04fbcc05a3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 07:44:45 +0800 Subject: [PATCH 456/941] Update dependency gradle to v8.14.1 (#1435) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ca025c83a..002b867c4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 33ab3fa06a32210d00a1ae05b86f39dc8a8c303c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 23 May 2025 21:58:01 +0800 Subject: [PATCH 457/941] Update xmlunit to v2.10.2 (#1437) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 159ea443a..05843cf99 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "2.2.0-RC" moshi = "1.15.2" pluginPublish = "1.3.1" -xmlunit = "2.10.1" +xmlunit = "2.10.2" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" From 01dd8149fc3cf592a1ed5fc1d049571dc7a5abde Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 26 May 2025 16:42:04 +0800 Subject: [PATCH 458/941] Remove explicit xmlunit-core This reverts parts of 1d7b0863. --- build.gradle.kts | 3 +-- gradle/libs.versions.toml | 4 +--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3656fdb41..896dc162c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -115,8 +115,7 @@ dependencies { testing.suites { getByName("test") { dependencies { - implementation(libs.xmlunit.core) - implementation(libs.xmlunit.legacy) + implementation(libs.xmlunit) } } register("documentTest") { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 05843cf99..96895ad93 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,6 @@ kotlin = "2.2.0-RC" moshi = "1.15.2" pluginPublish = "1.3.1" -xmlunit = "2.10.2" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" @@ -15,8 +14,7 @@ jdependency = "org.vafer:jdependency:2.12" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" -xmlunit-core = { module = "org.xmlunit:xmlunit-core", version.ref = "xmlunit" } -xmlunit-legacy = { module = "org.xmlunit:xmlunit-legacy", version.ref = "xmlunit" } +xmlunit = "org.xmlunit:xmlunit-legacy:2.10.2" moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } From 3d2e543bc501907bed59dfcd523288120194d7db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 27 May 2025 13:15:59 +0000 Subject: [PATCH 459/941] Update plugin com.gradle.develocity to v4.0.2 (#1439) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 4eca2b2cf..0d136f4c5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "4.0.1" + id("com.gradle.develocity") version "4.0.2" } develocity { From 472f7c807dfc4f56bd5ed65587bb3c1cfc18dfa7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 06:59:08 +0800 Subject: [PATCH 460/941] Update plugin spotless to v7.0.4 (#1442) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 96895ad93..cc24bb577 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,4 +36,4 @@ jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.32.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } -spotless = "com.diffplug.spotless:7.0.3" +spotless = "com.diffplug.spotless:7.0.4" From aa0b9309c4e5b7f062555d02d3fcb208c2828de1 Mon Sep 17 00:00:00 2001 From: Stefan Wolf Date: Wed, 28 May 2025 04:10:34 +0200 Subject: [PATCH 461/941] Allow using file trees of JARs together with the configuration cache (#1441) * Allow using file trees of JARs together with the configuration cache We are adding JARs from a directory that is built by a task to the `ShadowJar` by using `includedDependencies`. That doesn't seem to work when using the configuration cache, since the contents of the directory are evaluated at configuration time, causing them always to be empty. This PR fixes that by doing the actual resolution as late as in the task action, not when the `includedZipTrees` provider is being stored in the configuration cache. * Cleanups * Update changelog * Fix `includeFilesInTaskOutputDirectory` on Windows --------- Co-authored-by: Goooler --- api/shadow.api | 1 + docs/changes/README.md | 4 ++ .../gradle/plugins/shadow/JavaPluginTest.kt | 40 +++++++++++++++++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 11 ++--- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 741150c7b..596591c9e 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -182,6 +182,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar protected fun createCopyAction ()Lorg/gradle/api/internal/file/copy/CopyAction; public fun dependencies (Lorg/gradle/api/Action;)V public fun getApiJars ()Lorg/gradle/api/file/ConfigurableFileCollection; + protected abstract fun getArchiveOperations ()Lorg/gradle/api/file/ArchiveOperations; public fun getConfigurations ()Lorg/gradle/api/provider/SetProperty; public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; public fun getEnableRelocation ()Lorg/gradle/api/provider/Property; diff --git a/docs/changes/README.md b/docs/changes/README.md index e1488af62..03e3164a6 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -6,6 +6,10 @@ - Update start script templates. ([#1419](https://github.com/GradleUp/shadow/pull/1419)) +**Fixed** + +- Allow using file trees of JARs together with the configuration cache. ([#1441](https://github.com/GradleUp/shadow/pull/1441)) + ## [9.0.0-beta13](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta13) - 2025-04-29 **Changed** diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 0d9960b3f..188942dfe 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -724,4 +724,44 @@ class JavaPluginTest : BasePluginTest() { } } } + + @Issue( + "https://github.com/GradleUp/shadow/issues/1441", + ) + @Test + fun includeFilesInTaskOutputDirectory() { + // Create a build that has a task with jars in the output directory + projectScriptPath.appendText( + """ + def createJars = tasks.register('createJars') { + def artifactAJar = file('${artifactAJar.toUri().toURL().path}') + def artifactBJar = file('${artifactBJar.toUri().toURL().path}') + inputs.files(artifactAJar, artifactBJar) + def outputDir = file('${'$'}{buildDir}/jars') + outputs.dir(outputDir) + doLast { + artifactAJar.withInputStream { input -> + new File(outputDir, 'jarA.jar').withOutputStream { output -> + output << input + } + } + artifactBJar.withInputStream { input -> + new File(outputDir, 'jarB.jar').withOutputStream { output -> + output << input + } + } + } + } + $shadowJar { + includedDependencies.from(files(createJars).asFileTree) + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsOnly(*entriesInAB, *manifestEntries) + } + } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 2e9320734..dd00a5c41 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -22,12 +22,14 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransf import java.io.File import java.io.IOException import java.util.jar.JarFile +import javax.inject.Inject import kotlin.reflect.full.hasAnnotation import org.apache.tools.zip.Zip64Mode import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action import org.gradle.api.UncheckedIOException import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.ArchiveOperations import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.internal.file.FileResolver @@ -54,10 +56,6 @@ public abstract class ShadowJar : ShadowSpec { private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) - private val includedZipTrees = project.provider { - includedDependencies.files.map { project.zipTree(it) } - } - init { // https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L55-L64 duplicatesStrategy = DuplicatesStrategy.INCLUDE @@ -164,6 +162,9 @@ public abstract class ShadowJar : @Input // Trigger task executions after excludes changed. override fun getExcludes(): MutableSet = super.getExcludes() + @get:Inject + protected abstract val archiveOperations: ArchiveOperations + /** * Enable [minimizeJar], this equals to `minimizeJar.set(true)`. */ @@ -311,7 +312,7 @@ public abstract class ShadowJar : @TaskAction override fun copy() { - from(includedZipTrees.get()) + from(includedDependencies.files.map { archiveOperations.zipTree(it) }) injectMultiReleaseAttrIfPresent() super.copy() } From a3e3ce9f87a403b15575c6b89def417b4cab028d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 27 May 2025 22:24:14 -0400 Subject: [PATCH 462/941] Cleanup uri usages (#1443) * Use `invariantSeparatorsPathString` * Shorten maven repos --- .../jengelman/gradle/plugins/shadow/BasePluginTest.kt | 3 ++- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 7 ++++--- .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 4 +--- .../plugins/shadow/util/AppendableMavenRepository.kt | 7 +++---- 4 files changed, 10 insertions(+), 11 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 84183a432..c3f3fb4ee 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 @@ -30,6 +30,7 @@ import kotlin.io.path.createFile import kotlin.io.path.createTempDirectory import kotlin.io.path.deleteRecursively import kotlin.io.path.exists +import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.readText import kotlin.io.path.writeText import org.gradle.testkit.runner.BuildResult @@ -425,7 +426,7 @@ abstract class BasePluginTest { fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } fun implementationFiles(vararg paths: Path): String { - return paths.joinToString(System.lineSeparator()) { "implementation files('${it.toUri().toURL().path}')" } + return paths.joinToString(System.lineSeparator()) { "implementation files('${it.invariantSeparatorsPathString}')" } } inline fun transform( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 188942dfe..2acbbeac0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -30,6 +30,7 @@ import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.getStream import com.github.jengelman.gradle.plugins.shadow.util.runProcess import kotlin.io.path.appendText +import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.name import kotlin.io.path.outputStream import kotlin.io.path.writeText @@ -648,7 +649,7 @@ class JavaPluginTest : BasePluginTest() { projectScriptPath.appendText( """ $shadowJar { - from(file('${artifactAJar.toUri().toURL().path}')) { + from(file('${artifactAJar.invariantSeparatorsPathString}')) { into('META-INF') } from('Foo') { @@ -734,8 +735,8 @@ class JavaPluginTest : BasePluginTest() { projectScriptPath.appendText( """ def createJars = tasks.register('createJars') { - def artifactAJar = file('${artifactAJar.toUri().toURL().path}') - def artifactBJar = file('${artifactBJar.toUri().toURL().path}') + def artifactAJar = file('${artifactAJar.invariantSeparatorsPathString}') + def artifactBJar = file('${artifactBJar.invariantSeparatorsPathString}') inputs.files(artifactAJar, artifactBJar) def outputDir = file('${'$'}{buildDir}/jars') outputs.dir(outputDir) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index fdcf0f0ee..afb861d75 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -492,9 +492,7 @@ class PublishingTest : BasePluginTest() { $publicationsBlock } repositories { - maven { - url = '${remoteRepoPath.toUri()}' - } + maven { url = '${remoteRepoPath.toUri()}' } } } """.trimIndent() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index 5793e0ef2..72bb30583 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -5,6 +5,7 @@ import java.nio.file.Path import kotlin.io.path.createDirectories import kotlin.io.path.createFile import kotlin.io.path.exists +import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.isRegularFile import kotlin.io.path.name import kotlin.io.path.writeText @@ -48,9 +49,7 @@ class AppendableMavenRepository( ${modules.joinToString(System.lineSeparator()) { createPublication(it) }} } repositories { - maven { - url = '${root.toUri()}' - } + maven { url = '${root.toUri()}' } } } """.trimIndent(), @@ -81,7 +80,7 @@ class AppendableMavenRepository( artifactId = '$artifactId' groupId = '$groupId' version = '$version' - artifact '${outputJar.toUri().toURL().path}' + artifact '${outputJar.invariantSeparatorsPathString}' pom.withXml { xml -> def dependenciesNode = xml.asNode().get('dependencies') ?: xml.asNode().appendNode('dependencies') $nodes From efd23efad3d831b570bac6a2fd715a6c35d55bb3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 28 May 2025 01:27:40 -0400 Subject: [PATCH 463/941] Fallback RelocateClassContext and RelocatePathContext to data classes (#1445) --- api/shadow.api | 30 ++++++++----------- docs/changes/README.md | 1 + .../shadow/relocation/RelocationContext.kt | 6 ++-- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 596591c9e..8e84b492a 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -81,31 +81,25 @@ public abstract interface annotation class com/github/jengelman/gradle/plugins/s } public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext { - public static final synthetic fun box-impl (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; - public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String; + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;Ljava/lang/String;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext; public fun equals (Ljava/lang/Object;)Z - public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z - public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z public final fun getClassName ()Ljava/lang/String; public fun hashCode ()I - public static fun hashCode-impl (Ljava/lang/String;)I public fun toString ()Ljava/lang/String; - public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String; - public final synthetic fun unbox-impl ()Ljava/lang/String; } public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext { - public static final synthetic fun box-impl (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; - public static fun constructor-impl (Ljava/lang/String;)Ljava/lang/String; + public fun (Ljava/lang/String;)V + public final fun component1 ()Ljava/lang/String; + public final fun copy (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; + public static synthetic fun copy$default (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;Ljava/lang/String;ILjava/lang/Object;)Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext; public fun equals (Ljava/lang/Object;)Z - public static fun equals-impl (Ljava/lang/String;Ljava/lang/Object;)Z - public static final fun equals-impl0 (Ljava/lang/String;Ljava/lang/String;)Z public final fun getPath ()Ljava/lang/String; public fun hashCode ()I - public static fun hashCode-impl (Ljava/lang/String;)I public fun toString ()Ljava/lang/String; - public static fun toString-impl (Ljava/lang/String;)Ljava/lang/String; - public final synthetic fun unbox-impl ()Ljava/lang/String; } public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContextKt { @@ -118,8 +112,8 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/reloc public abstract fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; public abstract fun canRelocateClass (Ljava/lang/String;)Z public abstract fun canRelocatePath (Ljava/lang/String;)Z - public abstract fun relocateClass-XBGRxQs (Ljava/lang/String;)Ljava/lang/String; - public abstract fun relocatePath-bvWaKNU (Ljava/lang/String;)Ljava/lang/String; + public abstract fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; + public abstract fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; } public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator$Companion { @@ -143,8 +137,8 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public final fun getIncludes ()Ljava/util/Set; public fun hashCode ()I public fun include (Ljava/lang/String;)V - public fun relocateClass-XBGRxQs (Ljava/lang/String;)Ljava/lang/String; - public fun relocatePath-bvWaKNU (Ljava/lang/String;)Ljava/lang/String; + public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; + public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; } public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter : java/io/Serializable { diff --git a/docs/changes/README.md b/docs/changes/README.md index 03e3164a6..464106657 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -9,6 +9,7 @@ **Fixed** - Allow using file trees of JARs together with the configuration cache. ([#1441](https://github.com/GradleUp/shadow/pull/1441)) +- Fallback `RelocateClassContext` and `RelocatePathContext` to data classes. ([#1445](https://github.com/GradleUp/shadow/pull/1445)) ## [9.0.0-beta13](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta13) - 2025-04-29 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt index 48f8afa4e..b3c5c628a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt @@ -1,12 +1,10 @@ package com.github.jengelman.gradle.plugins.shadow.relocation -@JvmInline -public value class RelocateClassContext( +public data class RelocateClassContext( public val className: String, ) -@JvmInline -public value class RelocatePathContext( +public data class RelocatePathContext( public val path: String, ) From 421ebc072fa3b8b7df2b7ac882d51b0f73a12b84 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 28 May 2025 13:35:29 +0800 Subject: [PATCH 464/941] Update lint baseline --- gradle/lint-baseline.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index ad2f11d02..f491065a3 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -1,5 +1,5 @@ - + @@ -162,7 +162,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -173,7 +173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,7 +184,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> From 75799d63e3c827fc9f85ebdc412821271bbacf54 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 28 May 2025 13:43:01 +0800 Subject: [PATCH 465/941] Prepare version 9.0.0-beta14 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 464106657..bd81d9553 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta13...HEAD) - 2025-xx-xx +## [9.0.0-beta14](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta14) - 2025-05-28 **Changed** diff --git a/gradle.properties b/gradle.properties index dce25537f..ef3d98dfb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta14 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From b829b78e37d1f0befe314f1683abfdb7bb2944f0 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 28 May 2025 13:43:51 +0800 Subject: [PATCH 466/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index bd81d9553..6cf66cde8 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,5 +1,8 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta14...HEAD) - 2025-xx-xx + + ## [9.0.0-beta14](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta14) - 2025-05-28 **Changed** diff --git a/gradle.properties b/gradle.properties index ef3d98dfb..dce25537f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta14 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From 8fbfafd9bdef93b27d38a094e844e104b9ac64c2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 28 May 2025 05:42:42 -0400 Subject: [PATCH 467/941] Pin the plugin's Kotlin language level on 2.0 (#1448) --- build.gradle.kts | 4 ++-- docs/changes/README.md | 5 +++++ .../jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt | 1 - .../plugins/shadow/transformers/PropertiesFileTransformer.kt | 1 - 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 896dc162c..e35e2c90e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -36,8 +36,8 @@ kotlin { explicitApi() compilerOptions { // https://docs.gradle.org/current/userguide/compatibility.html#kotlin - @Suppress("DEPRECATION") // TODO: bump apiVersion to 2.0 to match Gradle 9.0 - apiVersion = KotlinVersion.KOTLIN_1_8 + apiVersion = KotlinVersion.KOTLIN_2_0 + languageVersion = apiVersion jvmTarget = JvmTarget.JVM_11 freeCompilerArgs.addAll( "-Xjvm-default=all", diff --git a/docs/changes/README.md b/docs/changes/README.md index 6cf66cde8..c41c2bb61 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,6 +2,11 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta14...HEAD) - 2025-xx-xx +**Fixed** + +- Pin the plugin's Kotlin language level on 2.0. ([#1448](https://github.com/GradleUp/shadow/pull/1448)) + The language level used in `9.0.0-beta14` is 2.2, which may cause compatibility issues for the plugins depending on + Shadow. ## [9.0.0-beta14](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta14) - 2025-05-28 diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt index 1af8dc917..d5e06ffc9 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt @@ -10,7 +10,6 @@ import org.junit.jupiter.api.io.TempDir class DocCodeSnippetTest { - @OptIn(ExperimentalStdlibApi::class) @TestFactory fun provideDynamicTests(@TempDir root: Path): List { val langExecutables = DslLang.entries.map { executor -> diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 4e6d6f377..4f331a0c5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -238,7 +238,6 @@ public open class PropertiesFileTransformer @Inject constructor( public companion object { @JvmStatic public fun from(value: String): MergeStrategy { - @OptIn(ExperimentalStdlibApi::class) return entries.find { it.name.equals(value, ignoreCase = true) } ?: throw IllegalArgumentException("Unknown merge strategy: $value") } From 49eab000244caec73e6f1529b2b24c4bb005bb57 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 28 May 2025 05:52:46 -0400 Subject: [PATCH 468/941] Enable allWarningsAsErrors for Kotlin (#1447) --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index e35e2c90e..cab91ab48 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -35,6 +35,7 @@ java { kotlin { explicitApi() compilerOptions { + allWarningsAsErrors = true // https://docs.gradle.org/current/userguide/compatibility.html#kotlin apiVersion = KotlinVersion.KOTLIN_2_0 languageVersion = apiVersion From 4f3ce566c9973b3b8ece410d92e23acef054b929 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 28 May 2025 17:46:02 +0800 Subject: [PATCH 469/941] Prepare version 9.0.0-beta15 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index c41c2bb61..6a1b22b21 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta14...HEAD) - 2025-xx-xx +## [9.0.0-beta15](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta15) - 2025-05-28 **Fixed** diff --git a/gradle.properties b/gradle.properties index dce25537f..e639b911a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta15 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From df5c158596a8847a37f5baaf6f3f7ff3226f0b97 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 28 May 2025 13:43:51 +0800 Subject: [PATCH 470/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 6a1b22b21..bc58d41ca 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,5 +1,8 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta15...HEAD) - 2025-xx-xx + + ## [9.0.0-beta15](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta15) - 2025-05-28 **Fixed** diff --git a/gradle.properties b/gradle.properties index e639b911a..dce25537f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta15 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From 06a8f9361a525ec1f81a3fe8628d0816f296dc57 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 28 May 2025 07:05:51 -0400 Subject: [PATCH 471/941] Restore removed Named from ResourceTransformer (#1449) * Revert "Remove Named from the parents of Transformer" This reverts commit 1bedb4c6 * Fix conflicts * Update changelog --- api/shadow.api | 7 ++++++- docs/changes/README.md | 3 +++ .../plugins/shadow/transformers/ResourceTransformer.kt | 7 ++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 8e84b492a..bea4872d9 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -238,6 +238,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec$D public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun hasTransformedResource ()Z public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V @@ -298,6 +299,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Compo public class com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun getName ()Ljava/lang/String; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun getResource ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z @@ -329,6 +331,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/IncludeReso public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public fun getFile ()Lorg/gradle/api/file/RegularFileProperty; + public fun getName ()Ljava/lang/String; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun getResource ()Lorg/gradle/api/provider/Property; public fun hasTransformedResource ()Z @@ -398,10 +401,11 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Prope public final fun from (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; } -public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { +public abstract interface class com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer : org/gradle/api/Named { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer$Companion; public abstract fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z public static fun create (Ljava/lang/Class;Lorg/gradle/api/model/ObjectFactory;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer; + public fun getName ()Ljava/lang/String; public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public abstract fun hasTransformedResource ()Z public abstract fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V @@ -417,6 +421,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Resou } public final class com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer$DefaultImpls { + public static fun getName (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Ljava/lang/String; public static fun getObjectFactory (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Lorg/gradle/api/model/ObjectFactory; } diff --git a/docs/changes/README.md b/docs/changes/README.md index bc58d41ca..6f96ab356 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,6 +2,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta15...HEAD) - 2025-xx-xx +**Fixed** + +- Restore removed `Named` from `ResourceTransformer`. ([#1449](https://github.com/GradleUp/shadow/pull/1449)) ## [9.0.0-beta15](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta15) - 2025-05-28 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt index d35c4acbc..8edcef93c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.relocation.CacheableRelocator import java.io.IOException import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.Named import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.Internal @@ -15,7 +16,7 @@ import org.gradle.api.tasks.Internal * @author John Engelman */ @JvmDefaultWithCompatibility -public interface ResourceTransformer { +public interface ResourceTransformer : Named { public fun canTransformResource(element: FileTreeElement): Boolean @Throws(IOException::class) @@ -26,6 +27,10 @@ public interface ResourceTransformer { @Throws(IOException::class) public fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) + @Suppress("unused") // Used by Gradle side, see https://github.com/GradleUp/shadow/pull/1289#issuecomment-2915738983. + @Internal + override fun getName(): String = this::class.java.simpleName + /** * This is used for creating Gradle's lazy properties in the subclass, Shadow's build-in transformers that depend on * this have been injected via [ObjectFactory.newInstance]. Custom transformers should implement or inject From f2543d740bd2fcfbe9114fdbd3a3fee83b35452a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 May 2025 09:20:56 +0800 Subject: [PATCH 472/941] Update plugin android-lint to v8.10.1 (#1450) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index cc24bb577..ea8f7d11c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.10.0" +android-lint = "com.android.lint:8.10.1" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.32.0" From 5589734e2e89950153a22e9385a0ea0f51cf82b3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 30 May 2025 13:54:27 +0000 Subject: [PATCH 473/941] Update dependency org.junit:junit-bom to v5.13.0 (#1452) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ea8f7d11c..417c8cafa 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.6.0" -junit-bom = "org.junit:junit-bom:5.12.2" +junit-bom = "org.junit:junit-bom:5.13.0" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From 0bc5cc9fa7b4d78d600b8e85972f4d2fba9440aa Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 30 May 2025 21:37:32 -0400 Subject: [PATCH 474/941] Note the changes for duplicatesStrategy in 9.0.0-beta9 (#1451) --- docs/changes/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 6f96ab356..2ae9b0d9d 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -132,7 +132,9 @@ Options: **Fixed** -- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + Shadow recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. + Now we honor `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. - Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) **Removed** From 248eaf0059682d682087c1e4b8772d66fe1ffa56 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 3 Jun 2025 04:04:44 -0400 Subject: [PATCH 475/941] Document Android Fused Library plugin (#1454) https://developer.android.com/build/publish-library/fused-library --- docs/android-plugins/README.md | 5 +++++ mkdocs.yml | 1 + 2 files changed, 6 insertions(+) create mode 100644 docs/android-plugins/README.md diff --git a/docs/android-plugins/README.md b/docs/android-plugins/README.md new file mode 100644 index 000000000..b3ea1ddd3 --- /dev/null +++ b/docs/android-plugins/README.md @@ -0,0 +1,5 @@ +# Integrating with Android Plugins + +Now you can bundle and publish multiple Android libraries as one with +[Android Fused Library Plugin](https://developer.android.com/build/publish-library/fused-library), which is backed by +Android official. diff --git a/mkdocs.yml b/mkdocs.yml index 7b80bb20d..6623e0c17 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -71,6 +71,7 @@ nav: - 'Application Plugin': application-plugin/README.md - 'Kotlin Plugins': kotlin-plugins/README.md - 'Groovy and Scala Plugins': groovy-and-scala-plugins/README.md + - 'Android Plugins': android-plugins/README.md - 'Publishing': publishing/README.md - 'Multi-Project': multi-project/README.md - 'Gradle Plugins': gradle-plugins/README.md From 3a4c27bfe5f88f23f10309e808d23e859d4863ee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Jun 2025 15:21:06 +0000 Subject: [PATCH 476/941] Update kotlin monorepo to v2.2.0-RC2 (#1457) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 417c8cafa..78c142984 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.2.0-RC" +kotlin = "2.2.0-RC2" moshi = "1.15.2" pluginPublish = "1.3.1" From aa31a2b02fd1612025704491bfff62d4a1711f51 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 5 Jun 2025 09:29:45 +0800 Subject: [PATCH 477/941] Use new jvmDefault flag https://kotlinlang.org/docs/whatsnew-eap.html?utm_campaign=kotlin-releases&utm_content=2-2-0-RC2&utm_medium=social&utm_source=twitter#changes-to-default-method-generation-for-interface-functions --- build.gradle.kts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cab91ab48..2229311a6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,6 +4,7 @@ import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME +import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion @@ -40,9 +41,7 @@ kotlin { apiVersion = KotlinVersion.KOTLIN_2_0 languageVersion = apiVersion jvmTarget = JvmTarget.JVM_11 - freeCompilerArgs.addAll( - "-Xjvm-default=all", - ) + jvmDefault = JvmDefaultMode.NO_COMPATIBILITY } } From 17dc709496375c23bd5dbbd8f44ce25f4dad8ea1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 5 Jun 2025 22:30:17 +0800 Subject: [PATCH 478/941] Update dependency gradle to v8.14.2 (#1459) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 002b867c4..ff23a68d7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 58052b2510128c3cdc39694edfee76b417993c3b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 6 Jun 2025 08:42:20 -0400 Subject: [PATCH 479/941] Set up release commenter (#1460) https://github.com/apexskier/github-release-commenter --- .github/workflows/commenter.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/commenter.yml diff --git a/.github/workflows/commenter.yml b/.github/workflows/commenter.yml new file mode 100644 index 000000000..59109616f --- /dev/null +++ b/.github/workflows/commenter.yml @@ -0,0 +1,19 @@ +name: Commenter + +on: + release: + types: [ published ] + +permissions: # required if repository sets restricted permissions for token, see https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token + issues: write # required if active on issues + pull-requests: write # required if active on pull requests + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: apexskier/github-release-commenter@v1 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + comment-template: | + Release {release_link} addresses this. From 1d2671d0d0416f1ff8c3a0d998b2ca9b8424b004 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 6 Jun 2025 12:45:15 +0000 Subject: [PATCH 480/941] Update dependency org.apache.maven:maven-model-builder to v3.9.10 (#1461) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 78c142984..930ab357d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ pluginPublish = "1.3.1" apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.19.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" -apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.9" +apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.10" asm = "org.ow2.asm:asm-commons:9.7.1" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.12" From fc52013a75a598ad7141ebd99b83415421b7b899 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 7 Jun 2025 18:34:03 +0800 Subject: [PATCH 481/941] Update dependency org.junit:junit-bom to v5.13.1 (#1462) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 930ab357d..9b26bd5b1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.6.0" -junit-bom = "org.junit:junit-bom:5.13.0" +junit-bom = "org.junit:junit-bom:5.13.1" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From 21baa181347f78fd3decbf9a43b28b61b3bfc2ee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 14 Jun 2025 01:05:03 +0000 Subject: [PATCH 482/941] Update ASM and jdependency to support Java 25 (#1380) * Update ASM and jdependency * Update changelog --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- docs/changes/README.md | 4 ++++ gradle/libs.versions.toml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 2ae9b0d9d..79b1c3224 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,6 +2,10 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta15...HEAD) - 2025-xx-xx +**Changed** + +- Update ASM to 9.8 to support Java 25. ([#1380](https://github.com/GradleUp/shadow/pull/1380)) + **Fixed** - Restore removed `Named` from `ResourceTransformer`. ([#1449](https://github.com/GradleUp/shadow/pull/1449)) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9b26bd5b1..f0ee64d38 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,9 +8,9 @@ apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.19.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.10" -asm = "org.ow2.asm:asm-commons:9.7.1" +asm = "org.ow2.asm:asm-commons:9.8" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. -jdependency = "org.vafer:jdependency:2.12" +jdependency = "org.vafer:jdependency:2.13" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" From a4e51260a19d0e8ff8f8fdb8049fce13e8304038 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 14 Jun 2025 09:43:28 +0800 Subject: [PATCH 483/941] Note the removal of TransformerContext.getEntryTimestamp (#1465) --- docs/changes/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index 79b1c3224..ba68b4516 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -147,6 +147,7 @@ Options: - **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) - **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- **BREAKING CHANGE:** Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) ## [9.0.0-beta8](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8) - 2025-02-08 From 0448b5536222f8eb539be5986e21ce6db45d3c2d Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 14 Jun 2025 09:45:20 +0800 Subject: [PATCH 484/941] Prepare version 9.0.0-beta16 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ba68b4516..8bbb94fcd 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta15...HEAD) - 2025-xx-xx +## [9.0.0-beta16](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta16) - 2025-06-14 **Changed** diff --git a/gradle.properties b/gradle.properties index dce25537f..549aab621 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta16 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From 66ea321f8b38edde5424dcad5a28dad158c17ace Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 14 Jun 2025 09:45:58 +0800 Subject: [PATCH 485/941] Prepare next development version --- docs/changes/README.md | 4 ++++ gradle.properties | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 8bbb94fcd..d5e5890f9 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,5 +1,9 @@ # Change Log + +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta16...HEAD) - 2025-xx-xx + + ## [9.0.0-beta16](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta16) - 2025-06-14 **Changed** diff --git a/gradle.properties b/gradle.properties index 549aab621..dce25537f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta16 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From c6afc2a4c1be01cf9bef835145045d173b533358 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 11:22:09 +0000 Subject: [PATCH 486/941] Update kotlin monorepo to v2.2.0-RC3 (#1466) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f0ee64d38..a5e4ad6cd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.2.0-RC2" +kotlin = "2.2.0-RC3" moshi = "1.15.2" pluginPublish = "1.3.1" From 6340c2207c2b189e2bdd3e7dee1dd89eefc4b538 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Jun 2025 23:00:42 +0000 Subject: [PATCH 487/941] Update dependency org.apache.logging.log4j:log4j-core to v2.25.0 (#1467) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a5e4ad6cd..c59ba6b23 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ pluginPublish = "1.3.1" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.19.0" -apache-log4j = "org.apache.logging.log4j:log4j-core:2.24.3" +apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.0" apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.10" asm = "org.ow2.asm:asm-commons:9.8" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. From 341d1cba08d015c24a39d20f85326149a655ac2e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 17 Jun 2025 09:45:27 +0800 Subject: [PATCH 488/941] Delete .github/workflows/commenter.yml --- .github/workflows/commenter.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/commenter.yml diff --git a/.github/workflows/commenter.yml b/.github/workflows/commenter.yml deleted file mode 100644 index 59109616f..000000000 --- a/.github/workflows/commenter.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Commenter - -on: - release: - types: [ published ] - -permissions: # required if repository sets restricted permissions for token, see https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token - issues: write # required if active on issues - pull-requests: write # required if active on pull requests - -jobs: - release: - runs-on: ubuntu-latest - steps: - - uses: apexskier/github-release-commenter@v1 - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - comment-template: | - Release {release_link} addresses this. From 63ac51019fdc48cf44dfc19e9adcf6e140fda806 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 18 Jun 2025 16:52:21 +0800 Subject: [PATCH 489/941] Update RELEASING.md --- RELEASING.md | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/RELEASING.md b/RELEASING.md index 186bc6728..f992e15ab 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -7,30 +7,27 @@ 2. Add a link URL to ensure the header link works. 3. Add a new `Unreleased` section to the top. -3. Update the `README.md` so the "Download" section reflects the new release version and the - snapshot section reflects the next "SNAPSHOT" version. - -4. Commit +3. Commit ```sh git commit -am "Prepare version X.Y.Z" ``` -5. Tag +4. Tag ```sh git tag -am "Version X.Y.Z" X.Y.Z ``` -6. Update the `VERSION_NAME` in `gradle.properties` to the next "SNAPSHOT" version. +5. Update the `VERSION_NAME` in `gradle.properties` to the next "SNAPSHOT" version. -7. Commit +6. Commit ```sh git commit -am "Prepare next development version" ``` -8. Push! +7. Push! ```sh git push && git push --tags From 62cccf4ee0eeaf922d256554263a866369883fe4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 18 Jun 2025 17:29:04 +0800 Subject: [PATCH 490/941] Gradle 9.0.0 RC1 (#1468) * Bump https://docs.gradle.org/9.0.0-rc-1/release-notes.html * Fix `dependencyProjectCompat` https://github.com/gradle/gradle/commit/3a27d546a713568343413f622c872b2f052ea757 * Replace `org.gradle.api.UncheckedIOException` https://github.com/gradle/gradle/commit/795fa937d3096a343938d7d5992ac326b179d21b * Update baseline * Update changelog --- docs/changes/README.md | 3 +++ gradle/lint-baseline.xml | 10 +++++----- gradle/wrapper/gradle-wrapper.jar | Bin 43764 -> 45457 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- .../plugins/shadow/internal/GradleCompat.kt | 3 +-- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 3 +-- 7 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index d5e5890f9..563668fe1 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta16...HEAD) - 2025-xx-xx +**Fixed** + +- Fix compatibility for Gradle 9.0.0 RC1. ([#1468](https://github.com/GradleUp/shadow/pull/1468)) ## [9.0.0-beta16](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta16) - 2025-06-14 diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index f491065a3..9ed89eb90 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -1,5 +1,5 @@ - + @@ -162,7 +162,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -173,7 +173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,7 +184,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baabb587c669f562ae36f953de2481846..8bdaf60c75ab801e22807dde59e12a8735a34077 100644 GIT binary patch delta 37256 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 delta 35551 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ff23a68d7..407c905d9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-rc-1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a936..ef07e0162 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index e7e7299b8..c77a97790 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -104,7 +104,6 @@ internal fun ProjectDependency.dependencyProjectCompat(project: Project): Projec return if (GradleVersion.current() >= GradleVersion.version("8.11")) { project.project(path) } else { - @Suppress("DEPRECATION") - dependencyProject + return this::class.java.getDeclaredMethod("getDependencyProject").invoke(this) as Project } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index dd00a5c41..f4be0c5dd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -27,7 +27,6 @@ import kotlin.reflect.full.hasAnnotation import org.apache.tools.zip.Zip64Mode import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action -import org.gradle.api.UncheckedIOException import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ArchiveOperations import org.gradle.api.file.ConfigurableFileCollection @@ -330,7 +329,7 @@ public abstract class ShadowJar : setMethod(entryCompressionMethod) } } catch (e: Exception) { - throw UncheckedIOException("Unable to create ZIP output stream for file $destination.", e) + throw IOException("Unable to create ZIP output stream for file $destination.", e) } } val unusedClasses = if (minimizeJar.get()) { From 3ab2d148e56b6bfd81466378374a5e4fc6deb208 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 18 Jun 2025 17:32:06 +0800 Subject: [PATCH 491/941] Prepare version 9.0.0-beta17 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 563668fe1..e1804f643 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta16...HEAD) - 2025-xx-xx +## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18 **Fixed** diff --git a/gradle.properties b/gradle.properties index dce25537f..c84b55f44 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-beta17 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From 4776b8551497d56a156423abcb42488f8c07da5b Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 18 Jun 2025 17:32:49 +0800 Subject: [PATCH 492/941] Prepare next development version --- docs/changes/README.md | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index e1804f643..86efe3a34 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,5 +1,7 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta17...HEAD) - 2025-xx-xx + ## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18 diff --git a/gradle.properties b/gradle.properties index c84b55f44..dce25537f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-beta17 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From bf1bd39231e16434963bcda02748127bbbff03f1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 08:32:10 +0800 Subject: [PATCH 493/941] Update dependency org.xmlunit:xmlunit-legacy to v2.10.3 (#1471) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c59ba6b23..91f54bcc0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -14,7 +14,7 @@ jdependency = "org.vafer:jdependency:2.13" jdom2 = "org.jdom:jdom2:2.0.6.1" plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" -xmlunit = "org.xmlunit:xmlunit-legacy:2.10.2" +xmlunit = "org.xmlunit:xmlunit-legacy:2.10.3" moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } From 2def703d1a3c505253e19be14fc53b09274e89e9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 22 Jun 2025 23:25:54 +0800 Subject: [PATCH 494/941] Replace pluginPublish with java-gradle-plugin (#1474) Workaround for vanniktech/gradle-maven-publish-plugin issues 926. --- build.gradle.kts | 2 +- gradle/libs.versions.toml | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2229311a6..ec67e97f0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,8 +14,8 @@ plugins { alias(libs.plugins.jetbrains.bcv) alias(libs.plugins.jetbrains.dokka) alias(libs.plugins.mavenPublish) - alias(libs.plugins.pluginPublish) alias(libs.plugins.spotless) + `java-gradle-plugin` } version = providers.gradleProperty("VERSION_NAME").get() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 91f54bcc0..2cf711200 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,6 @@ [versions] kotlin = "2.2.0-RC3" moshi = "1.15.2" -pluginPublish = "1.3.1" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" @@ -20,7 +19,7 @@ moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "mosh foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:1.0.0" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } -pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } +pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.1" androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. @@ -35,5 +34,4 @@ android-lint = "com.android.lint:8.10.1" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.32.0" -pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } spotless = "com.diffplug.spotless:7.0.4" From 8bb9faefb420c10fd42e1131e79074d2501a4856 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 16:53:01 +0000 Subject: [PATCH 495/941] Update plugin mavenPublish to v0.33.0 (#1475) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2cf711200..e4fb5adb5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,5 +33,5 @@ kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.10.1" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" -mavenPublish = "com.vanniktech.maven.publish:0.32.0" +mavenPublish = "com.vanniktech.maven.publish:0.33.0" spotless = "com.diffplug.spotless:7.0.4" From 08d35a81fcc4863ae6657ee08bd87bb1af33c1c3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 23 Jun 2025 11:44:07 +0800 Subject: [PATCH 496/941] Note the behavior change for ShadowTask.from (#1473) --- docs/changes/README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index 86efe3a34..305326a17 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -140,6 +140,21 @@ Options: - Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) - **BREAKING CHANGE:** Move `DependencyFilter` from `com.github.jengelman.gradle.plugins.shadow.internal` into `com.github.jengelman.gradle.plugins.shadow.tasks`. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) +- **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial + issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip + the files, only copy the files as-is. If you still want to shadow the unzipped files, try out something like: + ```kotlin + tasks.shadowJar { + from(zipTree(files('path/to/your/file.zip'))) + } + ``` + or + ```kotlin + dependencies { + implementation(files('path/to/your/file.zip')) + } + ``` - Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) From 5d4456a92e5ed1cc03401fe7f2bc46624f58692c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 23 Jun 2025 12:06:06 +0800 Subject: [PATCH 497/941] Remove UnusedTracker.forProject (#1476) --- .../plugins/shadow/internal/UnusedTracker.kt | 14 +++----------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 6 +++++- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index 099fe20f6..f6dfc4ced 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -12,8 +12,8 @@ import org.vafer.jdependency.Clazzpath import org.vafer.jdependency.ClazzpathUnit /** Tracks unused classes in the project classpath. */ -internal class UnusedTracker private constructor( - classDirs: Iterable, +internal class UnusedTracker( + sourceSetsClassesDirs: Iterable, classJars: FileCollection, @get:InputFiles val toMinimize: FileCollection, ) { @@ -21,7 +21,7 @@ internal class UnusedTracker private constructor( private val cp = Clazzpath() init { - projectUnits = classDirs.map { cp.addClazzpathUnit(it) } + classJars.map { cp.addClazzpathUnit(it) } + projectUnits = sourceSetsClassesDirs.map { cp.addClazzpathUnit(it) } + classJars.map { cp.addClazzpathUnit(it) } } fun findUnused(): Set { @@ -40,14 +40,6 @@ internal class UnusedTracker private constructor( } companion object { - fun forProject( - apiJars: FileCollection, - sourceSetsClassesDirs: Iterable, - toMinimize: FileCollection, - ): UnusedTracker { - return UnusedTracker(sourceSetsClassesDirs, apiJars, toMinimize) - } - fun getApiJarsFromProject(project: Project): FileCollection { val apiDependencies = project.configurations.findByName("api")?.dependencies ?: return project.files() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index f4be0c5dd..28422774c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -333,7 +333,11 @@ public abstract class ShadowJar : } } val unusedClasses = if (minimizeJar.get()) { - val unusedTracker = UnusedTracker.forProject(apiJars, sourceSetsClassesDirs.files, toMinimize) + val unusedTracker = UnusedTracker( + sourceSetsClassesDirs = sourceSetsClassesDirs.files, + classJars = apiJars, + toMinimize = toMinimize, + ) includedDependencies.files.forEach { unusedTracker.addDependency(it) } From b8d9e57589b0c9be4f38c59aff4345538ed5a847 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 23 Jun 2025 12:22:37 +0000 Subject: [PATCH 498/941] Update kotlin monorepo to v2.2.0 (#1477) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e4fb5adb5..523168804 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.2.0-RC3" +kotlin = "2.2.0" moshi = "1.15.2" [libraries] From 1023196183c2872467fcd84adb379fbe431bd506 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 24 Jun 2025 09:31:11 +0800 Subject: [PATCH 499/941] Revert "Replace pluginPublish with java-gradle-plugin (#1474)" We need the `publishPlugins` for publishing to Gradle Plugin Portal. This reverts commit 2def703d --- build.gradle.kts | 2 +- gradle/libs.versions.toml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index ec67e97f0..2229311a6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -14,8 +14,8 @@ plugins { alias(libs.plugins.jetbrains.bcv) alias(libs.plugins.jetbrains.dokka) alias(libs.plugins.mavenPublish) + alias(libs.plugins.pluginPublish) alias(libs.plugins.spotless) - `java-gradle-plugin` } version = providers.gradleProperty("VERSION_NAME").get() diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 523168804..ae417b8c2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,7 @@ [versions] kotlin = "2.2.0" moshi = "1.15.2" +pluginPublish = "1.3.1" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" @@ -19,7 +20,7 @@ moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "mosh foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:1.0.0" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } -pluginPublish = "com.gradle.publish:plugin-publish-plugin:1.3.1" +pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. @@ -34,4 +35,5 @@ android-lint = "com.android.lint:8.10.1" jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.33.0" +pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } spotless = "com.diffplug.spotless:7.0.4" From b1cac71d15259ff1f007a9a098c3f835dac59bf4 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 24 Jun 2025 09:40:52 +0800 Subject: [PATCH 500/941] Sync the changelog for 8.3.7 Refs badabadc2b6c8de66178426974808a1cf12f01f5. --- docs/changes/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index 305326a17..590ea8d30 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -299,6 +299,12 @@ Options: - Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) +## [8.3.7](https://github.com/GradleUp/shadow/releases/tag/8.3.7) - 2025-06-24 + +**Fixed** + +- Fix compatibility for Gradle 9.0.0 RC1. ([#1470](https://github.com/GradleUp/shadow/pull/1470)) + ## [8.3.6](https://github.com/GradleUp/shadow/releases/tag/8.3.6) - 2025-02-02 **Added** From 5952ce9d36c9bdf6435df7628ad52972505354c2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 24 Jun 2025 11:06:33 +0800 Subject: [PATCH 501/941] Migrate BCV to the Kotlin's builtin (#1481) https://kotlinlang.org/docs/whatsnew22.html#binary-compatibility-validation-included-in-kotlin-gradle-plugin --- build.gradle.kts | 13 +++++++++++-- gradle/libs.versions.toml | 1 - 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2229311a6..317fa30e9 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,14 +4,15 @@ import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME +import org.gradle.kotlin.dsl.kotlin import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion +import org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation plugins { alias(libs.plugins.kotlin.jvm) alias(libs.plugins.android.lint) - alias(libs.plugins.jetbrains.bcv) alias(libs.plugins.jetbrains.dokka) alias(libs.plugins.mavenPublish) alias(libs.plugins.pluginPublish) @@ -35,6 +36,10 @@ java { kotlin { explicitApi() + @OptIn(ExperimentalAbiValidation::class) + abiValidation { + enabled = true + } compilerOptions { allWarningsAsErrors = true // https://docs.gradle.org/current/userguide/compatibility.html#kotlin @@ -213,7 +218,11 @@ tasks.validatePlugins { } tasks.check { - dependsOn(tasks.withType()) + dependsOn( + tasks.withType(), + // TODO: https://youtrack.jetbrains.com/issue/KT-78525 + tasks.checkLegacyAbi, + ) } tasks.register("downloadStartScripts") { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ae417b8c2..f4e15a808 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.10.1" -jetbrains-bcv = "org.jetbrains.kotlinx.binary-compatibility-validator:0.17.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.33.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From a4efeca1eebea439985fc1d7119b77c12fdafe68 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 14:50:46 +0000 Subject: [PATCH 502/941] Update dependency org.junit:junit-bom to v5.13.2 (#1482) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f4e15a808..f6d67f698 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.6.0" -junit-bom = "org.junit:junit-bom:5.13.1" +junit-bom = "org.junit:junit-bom:5.13.2" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From 442c71e724d407af8ac8f8169fb840927c09cee1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Jun 2025 09:11:14 +0800 Subject: [PATCH 503/941] Update plugin android-lint to v8.11.0 (#1484) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f6d67f698..32c92c469 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.10.1" +android-lint = "com.android.lint:8.11.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.33.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From 08cfc46cf6ce03439e97d64d09cd2eeeb6852b5f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 25 Jun 2025 17:27:02 +0800 Subject: [PATCH 504/941] Note the plugins IDs (#1486) --- README.md | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index e9e846d88..bb8480ab6 100644 --- a/README.md +++ b/README.md @@ -3,10 +3,13 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. > [!NOTE]\ -> Previously this plugin was developed by [@johnrengelman](https://github.com/johnrengelman) and published under the ID [`com.github.johnrengelman.shadow`](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) -> before maintenance was transferred to the [GradleUp organization](https://github.com/GradleUp) to ensure future development, see [#908](https://github.com/GradleUp/shadow/issues/908). +> Previously this plugin was developed by [@johnrengelman](https://github.com/johnrengelman) and published under the +> ID [`com.github.johnrengelman.shadow`][johnrengelman's] +> before maintenance was transferred to the [GradleUp organization](https://github.com/GradleUp) to ensure future +> development, see [#908](https://github.com/GradleUp/shadow/issues/908). > -> If you are still using the old plugin ID in your build script, we recommend to switch to the new plugin ID [`com.gradleup.shadow`](https://plugins.gradle.org/plugin/com.gradleup.shadow) +> If you are still using the old plugin ID in your build script, we recommend to switch to the new plugin ID [ +`com.gradleup.shadow`][gradleup's] > and update to the latest version to receive all the latest bug fixes and improvements. ## Documentation @@ -24,11 +27,15 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. ## Compatibility Matrix -| Shadow Version | Min Gradle Version | Min Java Version | -|----------------|--------------------|------------------| -| 5.2.0 - 6.1.0 | 5.x - 6.x | 7 | -| 6.1.0+ | 6.x | 8 | -| 7.0.0+ | 7.x | 8 | -| 8.0.0+ | 8.0 | 8 | -| 8.3.0+ | 8.3 | 8 | -| 9.0.0+ | 8.3 | 11 | +| Shadow Version | Min Gradle Version | Min Java Version | Plugin ID | +|----------------|--------------------|------------------|------------------------------------------------------| +| 5.2.0 - 6.1.0 | 5.x - 6.x | 7 | [`com.github.johnrengelman.shadow`][johnrengelman's] | +| 6.1.0+ | 6.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | +| 7.0.0+ | 7.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | +| 8.0.0+ | 8.0 | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | +| 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | +| 9.0.0+ | 8.3 | 11 | [`com.gradleup.shadow`][gradleup's] | + + +[johnrengelman's]: https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow +[gradleup's]: https://plugins.gradle.org/plugin/com.gradleup.shadow From 68c9f64b53a607060dcdd4b420f40ae1b7dfdb2c Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 25 Jun 2025 18:00:24 +0800 Subject: [PATCH 505/941] Update the introduction page --- README.md | 1 + docs/README.md | 18 ++++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bb8480ab6..86223feae 100644 --- a/README.md +++ b/README.md @@ -37,5 +37,6 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. | 9.0.0+ | 8.3 | 11 | [`com.gradleup.shadow`][gradleup's] | + [johnrengelman's]: https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow [gradleup's]: https://plugins.gradle.org/plugin/com.gradleup.shadow diff --git a/docs/README.md b/docs/README.md index d6b86a2ec..1428492a0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,6 +1,5 @@

# Introduction @@ -13,14 +12,23 @@ dependent libraries into the output jar without incurring the I/O overhead of ex !!! warning "Plugin ID Change" Previously this plugin was developed by [@johnrengelman](https://github.com/johnrengelman) and published under the ID [ - `com.github.johnrengelman.shadow`](https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow) + `com.github.johnrengelman.shadow`][johnrengelman's] before maintenance was transferred to the [GradleUp organization](https://github.com/GradleUp) to ensure future development, see [#908](https://github.com/GradleUp/shadow/issues/908). If you are still using the old plugin ID in your build script, we recommend to switch to the new plugin ID [ - `com.gradleup.shadow`](https://plugins.gradle.org/plugin/com.gradleup.shadow) + `com.gradleup.shadow`][gradleup's] and update to the latest version to receive all the latest bug fixes and improvements. +| Shadow Version | Min Gradle Version | Min Java Version | Plugin ID | +|----------------|--------------------|------------------|------------------------------------------------------| +| 5.2.0 - 6.1.0 | 5.x - 6.x | 7 | [`com.github.johnrengelman.shadow`][johnrengelman's] | +| 6.1.0+ | 6.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | +| 7.0.0+ | 7.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | +| 8.0.0+ | 8.0 | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | +| 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | +| 9.0.0+ | 8.3 | 11 | [`com.gradleup.shadow`][gradleup's] | + ## Benefits of Shadow Shadowing a project output has 2 major use cases: @@ -61,3 +69,5 @@ library's dependencies will not conflict with the same dependency being declared [JarInputStream]: https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarInputStream.html [JarOutputStream]: https://docs.oracle.com/javase/8/docs/api/java/util/jar/JarOutputStream.html +[johnrengelman's]: https://plugins.gradle.org/plugin/com.github.johnrengelman.shadow +[gradleup's]: https://plugins.gradle.org/plugin/com.gradleup.shadow From 8eeb64e969ce68ba82d864602cc2d120898866bd Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 26 Jun 2025 16:06:17 +0800 Subject: [PATCH 506/941] Expose Ant as compile scope (#1488) As `org.apache.tools.zip.ZipOutputStream` is exposed in the public APIs: ```api public class com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V ... } ``` View the scope changes: ```diff diff --color=auto -r before/shadow-gradle-plugin-9.0.0-SNAPSHOT.module after/shadow-gradle-plugin-9.0.0-SNAPSHOT.module 27a28,36 > "dependencies": [ > { > "group": "org.apache.ant", > "module": "ant", > "version": { > "requires": "1.10.15" > } > } > ], 85,91d93 < "group": "org.apache.ant", < "module": "ant", < "version": { < "requires": "1.10.15" < } < }, < { 137a140,146 > } > }, > { > "group": "org.apache.ant", > "module": "ant", > "version": { > "requires": "1.10.15" diff --color=auto -r before/shadow-gradle-plugin-9.0.0-SNAPSHOT.pom after/shadow-gradle-plugin-9.0.0-SNAPSHOT.pom 40c40 < runtime --- > compile ``` --- build.gradle.kts | 2 +- docs/changes/README.md | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 317fa30e9..6cab2c928 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -101,7 +101,7 @@ publishing.publications.withType().configureEach { dependencies { compileOnly(libs.kotlin.kmp) - implementation(libs.apache.ant) + api(libs.apache.ant) // Types from Ant are exposed in the public API. implementation(libs.apache.commonsIo) implementation(libs.apache.log4j) implementation(libs.asm) diff --git a/docs/changes/README.md b/docs/changes/README.md index 590ea8d30..053b4e832 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,6 +2,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta17...HEAD) - 2025-xx-xx +**Changed** + +- Expose Ant as compile scope. ([#1487](https://github.com/GradleUp/shadow/pull/1487)) ## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18 From a10d8ffb339233c32458b979860dd4175929138c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 26 Jun 2025 16:13:21 +0800 Subject: [PATCH 507/941] Fix the changelog for PR 1488 --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 053b4e832..d1fc6133d 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -4,7 +4,7 @@ **Changed** -- Expose Ant as compile scope. ([#1487](https://github.com/GradleUp/shadow/pull/1487)) +- Expose Ant as `compile` scope. ([#1488](https://github.com/GradleUp/shadow/pull/1488)) ## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18 From f3b66b8d459d3cfd19b6b6ab0456b51a73c3493a Mon Sep 17 00:00:00 2001 From: Vladimir Sitnikov Date: Fri, 27 Jun 2025 10:11:15 +0300 Subject: [PATCH 508/941] Pin Java release on 11 (#1490) Uses `JavaCompile.options.release=11` and `-Xjdk-release=11` for properly targeting Java 11 release. See * https://www.morling.dev/blog/bytebuffer-and-the-dreaded-nosuchmethoderror * https://github.com/gradle/gradle/issues/34035 * https://github.com/Kotlin/api-guidelines/issues/44 --- build.gradle.kts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6cab2c928..708aadace 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,9 +29,8 @@ dokka { } } -java { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 +tasks.withType().configureEach { + options.release = 11 } kotlin { @@ -47,6 +46,7 @@ kotlin { languageVersion = apiVersion jvmTarget = JvmTarget.JVM_11 jvmDefault = JvmDefaultMode.NO_COMPATIBILITY + freeCompilerArgs.add("-Xjdk-release=11") } } From 701b5f7f6bf26ace8aed5ec91a04e39b478aa1dc Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 27 Jun 2025 15:13:01 +0800 Subject: [PATCH 509/941] Rearrange and comment --- build.gradle.kts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 708aadace..77b144c76 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -29,10 +29,6 @@ dokka { } } -tasks.withType().configureEach { - options.release = 11 -} - kotlin { explicitApi() @OptIn(ExperimentalAbiValidation::class) @@ -46,6 +42,7 @@ kotlin { languageVersion = apiVersion jvmTarget = JvmTarget.JVM_11 jvmDefault = JvmDefaultMode.NO_COMPATIBILITY + // Sync with `JavaCompile.options.release`. freeCompilerArgs.add("-Xjdk-release=11") } } @@ -205,6 +202,10 @@ kotlin.target.compilations { } } +tasks.withType().configureEach { + options.release = 11 +} + tasks.pluginUnderTestMetadata { // Plugins used in tests could be resolved in classpath. pluginClasspath.from( From e2a951060e052af78544cfa65916498fbdaa1c8a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 1 Jul 2025 15:43:19 +0800 Subject: [PATCH 510/941] Add functional tests for PropertiesFileTransformer (#1485) * Generate `PropertiesFileTransformerFunctionalTest` * Renames * Update `mergePropertiesWithSpecifiedCharset` * Update `mergePropertiesWithDifferentStrategies` * Cleanups * Use semicolon for not escaping * Cleanup `mergePropertiesWithMappings` * Note `CleanProperties` * Test `mergedPropertiesDontContainDateComment` --- .../PropertiesFileTransformerTest.kt | 164 ++++++++++++++++++ .../shadow/transformers/TransformersTest.kt | 1 - .../shadow/internal/CleanProperties.kt | 3 + 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt new file mode 100644 index 000000000..a050dd1f5 --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -0,0 +1,164 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy +import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.getContent +import kotlin.io.path.appendText +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource + +class PropertiesFileTransformerTest : BaseTransformerTest() { + + @ParameterizedTest + @EnumSource(MergeStrategy::class) + fun mergePropertiesWithDifferentStrategies(strategy: MergeStrategy) { + val one = buildJarOne { + insert("META-INF/test.properties", "key1=one\nkey2=one") + } + val two = buildJarTwo { + insert("META-INF/test.properties", "key2=two\nkey3=two") + } + projectScriptPath.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = """ + mergeStrategy = $mergeStrategyClassName.$strategy + mergeSeparator = ";" + paths = ["META-INF/test.properties"] + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + val expected = when (strategy) { + MergeStrategy.First -> arrayOf("key1=one", "key2=one", "key3=two") + MergeStrategy.Latest -> arrayOf("key1=one", "key2=two", "key3=two") + MergeStrategy.Append -> arrayOf("key1=one", "key2=one;two", "key3=two") + } + val content = outputShadowJar.use { it.getContent("META-INF/test.properties") } + assertThat(content).contains(*expected) + } + + @Test + fun mergePropertiesWithKeyTransformer() { + val one = buildJarOne { + insert("META-INF/test.properties", "foo=bar") + } + val two = buildJarTwo { + insert("META-INF/test.properties", "FOO=baz") + } + projectScriptPath.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = """ + mergeStrategy = $mergeStrategyClassName.Append + keyTransformer = { key -> key.toUpperCase() } + paths = ["META-INF/test.properties"] + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + val content = outputShadowJar.use { it.getContent("META-INF/test.properties") } + assertThat(content).contains("FOO=bar,baz") + } + + @Test + fun mergePropertiesWithSpecifiedCharset() { + val one = buildJarOne { + insert("META-INF/utf8.properties", "foo=第一") + } + val two = buildJarTwo { + insert("META-INF/utf8.properties", "foo=第二") + } + projectScriptPath.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = """ + mergeStrategy = $mergeStrategyClassName.Append + charsetName = "utf-8" + paths = ["META-INF/utf8.properties"] + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + val content = outputShadowJar.use { it.getContent("META-INF/utf8.properties") } + assertThat(content).contains("foo=第一,第二") + } + + @Test + fun mergePropertiesWithMappings() { + val one = buildJarOne { + insert("META-INF/foo.properties", "foo=1") + insert("META-INF/bar.properties", "bar=2") + } + val two = buildJarTwo { + insert("META-INF/foo.properties", "foo=3") + insert("META-INF/bar.properties", "bar=4") + } + projectScriptPath.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = """ + mappings = [ + "META-INF/foo.properties": ["mergeStrategy": "append", "mergeSeparator": ";"], + "META-INF/bar.properties": ["mergeStrategy": "latest"] + ] + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + getContent("META-INF/foo.properties").contains("foo=1;3") + getContent("META-INF/bar.properties").contains("bar=4") + } + } + + @Issue( + "https://github.com/GradleUp/shadow/issues/622", + "https://github.com/GradleUp/shadow/issues/856", + ) + @Test + fun mergedPropertiesDontContainDateComment() { + val one = buildJarOne { + insert("META-INF/test.properties", "foo=one") + } + val two = buildJarTwo { + insert("META-INF/test.properties", "foo=two") + } + projectScriptPath.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = """ + mergeStrategy = $mergeStrategyClassName.Append + paths = ["META-INF/test.properties"] + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + val content = outputShadowJar.use { it.getContent("META-INF/test.properties") } + assertThat(content.trimIndent()).isEqualTo( + """ + # + + foo=one,two + """.trimIndent(), + ) + } + + private companion object { + val mergeStrategyClassName = requireNotNull(MergeStrategy::class.java.canonicalName) + } +} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 2ed0f9019..6940adad5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -195,7 +195,6 @@ class TransformersTest : BaseTransformerTest() { "{ resource.set(\"test.file\"); file.fileValue(file(\"test/some.file\")) }" to IncludeResourceTransformer::class, "" to ManifestAppenderTransformer::class, "" to ManifestResourceTransformer::class, - "{ keyTransformer = { it.toLowerCase() } }" to PropertiesFileTransformer::class, ) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt index 13f6730f7..624aa422d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt @@ -6,6 +6,9 @@ import java.io.Writer import java.util.Date import java.util.Properties +/** + * Introduced in order to remove prepended timestamp when creating output stream. + */ internal class CleanProperties : Properties() { @Throws(IOException::class) override fun store(writer: Writer, comments: String) { From 181309a15aa405e958efe719c17066bb7a2c2fea Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 1 Jul 2025 17:40:44 +0800 Subject: [PATCH 511/941] Bump the min Gradle requirement to 8.11 and update test matrix (#1479) * Bump the min Gradle requirement to 8.11 * Update changelog * Update the workflow name * Update compatibility matrix * Cleanup --- .github/workflows/build.yml | 9 +++++- README.md | 2 +- build.gradle.kts | 6 ++++ docs/README.md | 2 +- docs/changes/README.md | 1 + .../shadow/snippet/SnippetExecutable.kt | 4 +++ .../gradle/plugins/shadow/BasePluginTest.kt | 4 +++ .../gradle/plugins/shadow/JavaPluginTest.kt | 31 ------------------- .../gradle/plugins/shadow/ShadowBasePlugin.kt | 4 +-- .../plugins/shadow/internal/GradleCompat.kt | 22 +------------ .../plugins/shadow/internal/UnusedTracker.kt | 2 +- 11 files changed, 29 insertions(+), 58 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cab16e3fb..790fbacb6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,11 +9,18 @@ on: jobs: build: + name: OS=${{ matrix.os }}, Java=${{ matrix.java }}, Gradle=${{ matrix.gradle }} strategy: matrix: os: [ ubuntu-latest, windows-latest ] # Always test on the latest version and some LTS. java: [ 17, 21, 24 ] + # Test on the minimum Gradle version and the latest. + gradle: [ 8.11, current ] + exclude: + # Gradle 8.11 doesn't support Java 24. + - gradle: 8.11 + java: 24 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -22,7 +29,7 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java }} - uses: gradle/actions/setup-gradle@v4 - - run: ./gradlew build + - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" # There's no need to run the update task frequently; the start scripts are typically updated with Gradle releases. update-start-scripts: diff --git a/README.md b/README.md index 86223feae..89e94bbdd 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. | 7.0.0+ | 7.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.0.0+ | 8.0 | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | -| 9.0.0+ | 8.3 | 11 | [`com.gradleup.shadow`][gradleup's] | +| 9.0.0+ | 8.11 | 11 | [`com.gradleup.shadow`][gradleup's] | diff --git a/build.gradle.kts b/build.gradle.kts index 77b144c76..4eb0faa95 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -167,6 +167,12 @@ testing.suites { } targets.configureEach { testTask { + val testGradleVersion = providers.gradleProperty("testGradleVersion").orNull.let { + if (it == null || it == "current") GradleVersion.current().version else it + } + logger.info("Using test Gradle version: $testGradleVersion") + systemProperty("TEST_GRADLE_VERSION", testGradleVersion) + maxParallelForks = Runtime.getRuntime().availableProcessors() } } diff --git a/docs/README.md b/docs/README.md index 1428492a0..fead7c937 100644 --- a/docs/README.md +++ b/docs/README.md @@ -27,7 +27,7 @@ dependent libraries into the output jar without incurring the I/O overhead of ex | 7.0.0+ | 7.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.0.0+ | 8.0 | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | -| 9.0.0+ | 8.3 | 11 | [`com.gradleup.shadow`][gradleup's] | +| 9.0.0+ | 8.11 | 11 | [`com.gradleup.shadow`][gradleup's] | ## Benefits of Shadow diff --git a/docs/changes/README.md b/docs/changes/README.md index d1fc6133d..b0205f029 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -4,6 +4,7 @@ **Changed** +- Bump the min Gradle requirement to 8.11. ([#1479](https://github.com/GradleUp/shadow/pull/1479)) - Expose Ant as `compile` scope. ([#1488](https://github.com/GradleUp/shadow/pull/1488)) ## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18 diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index 17130c574..df3d25ef7 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -65,6 +65,7 @@ sealed class SnippetExecutable : Executable { try { GradleRunner.create() + .withGradleVersion(testGradleVersion) .withProjectDir(projectRoot.toFile()) .withPluginClasspath() .forwardOutput() @@ -97,6 +98,9 @@ sealed class SnippetExecutable : Executable { } companion object { + private val testGradleVersion = System.getProperty("TEST_GRADLE_VERSION") + ?: error("TEST_GRADLE_VERSION system property is not set.") + fun create( lang: DslLang, snippet: String, 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 c3f3fb4ee..f5908ae2e 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 @@ -379,6 +379,7 @@ abstract class BasePluginTest { arguments: Iterable = emptyList(), projectDir: Path? = projectRoot, ): GradleRunner = GradleRunner.create() + .withGradleVersion(testGradleVersion) .forwardOutput() .withPluginClasspath() .withTestKitDir(testKitDir.toFile()) @@ -391,6 +392,9 @@ abstract class BasePluginTest { @Suppress("ConstPropertyName") companion object { + private val testGradleVersion = System.getProperty("TEST_GRADLE_VERSION") + ?: error("TEST_GRADLE_VERSION system property is not set.") + val testKitDir: Path = run { var gradleUserHome = System.getenv("GRADLE_USER_HOME") if (gradleUserHome == null) { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 2acbbeac0..2b116ba06 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -38,8 +38,6 @@ import org.gradle.api.plugins.JavaPlugin import org.gradle.api.tasks.bundling.Jar import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Test -import org.junit.jupiter.api.condition.DisabledForJreRange -import org.junit.jupiter.api.condition.JRE import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource @@ -92,35 +90,6 @@ class JavaPluginTest : BasePluginTest() { assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile) } - @Test - @DisabledForJreRange( - min = JRE.JAVA_21, - disabledReason = "Gradle 8.3 doesn't support Java 21.", - ) - fun compatibleWithMinGradleVersion() { - val mainClassEntry = writeClass(withImports = true) - projectScriptPath.appendText( - """ - dependencies { - implementation 'junit:junit:3.8.2' - } - """.trimIndent(), - ) - - run(shadowJarTask) { - it.withGradleVersion("8.3") - } - - assertThat(outputShadowJar).useAll { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) - } - } - @Test fun incompatibleWithLowerMinGradleVersion() { runWithFailure(shadowJarTask) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 77a6c09a7..4b8066c90 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -11,8 +11,8 @@ import org.gradle.util.GradleVersion public abstract class ShadowBasePlugin : Plugin { override fun apply(project: Project): Unit = with(project) { - if (GradleVersion.current() < GradleVersion.version("8.3")) { - throw GradleException("This version of Shadow supports Gradle 8.3+ only. Please upgrade.") + if (GradleVersion.current() < GradleVersion.version("8.11")) { + throw GradleException("This version of Shadow supports Gradle 8.11+ only. Please upgrade.") } @Suppress("DEPRECATION") extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index c77a97790..170668b21 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow.internal import org.gradle.api.Project import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.distribution.DistributionContainer import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.model.ObjectFactory @@ -18,7 +17,6 @@ import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.jvm.tasks.Jar import org.gradle.jvm.toolchain.JavaToolchainService -import org.gradle.util.GradleVersion /** * Return `runtimeClasspath` or `runtime` configuration. @@ -83,27 +81,9 @@ internal inline fun ObjectFactory.mapProperty( } } -/** - * TODO: this could be removed after bumping the min Gradle requirement to 8.8 or above. - */ internal inline fun ObjectFactory.fileCollection( path: () -> Any, ): ConfigurableFileCollection = fileCollection().apply { @Suppress("UnstableApiUsage") - if (GradleVersion.current() >= GradleVersion.version("8.8")) { - convention(path()) - } else { - setFrom(path()) - } -} - -/** - * TODO: this could be removed after bumping the min Gradle requirement to 8.11 or above. - */ -internal fun ProjectDependency.dependencyProjectCompat(project: Project): Project { - return if (GradleVersion.current() >= GradleVersion.version("8.11")) { - project.project(path) - } else { - return this::class.java.getDeclaredMethod("getDependencyProject").invoke(this) as Project - } + convention(path()) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index f6dfc4ced..b82aaeda7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -48,7 +48,7 @@ internal class UnusedTracker( apiDependencies.forEach { dep -> when (dep) { is ProjectDependency -> { - apiJars.addAll(getApiJarsFromProject(dep.dependencyProjectCompat(project))) + apiJars.addAll(getApiJarsFromProject(project.project(dep.path))) addJar(runtimeConfiguration, dep, apiJars) } is FileCollectionDependency -> { From e167474fbad0bba10f83c0fd5412062861126a67 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 1 Jul 2025 21:18:37 +0800 Subject: [PATCH 512/941] Sync the changelog for 8.3.8 Refs 6861d79fed99bbeb94576a467b60cc92daa31850. --- docs/changes/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index b0205f029..afa7fbea3 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -303,6 +303,16 @@ Options: - Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) +## [8.3.8](https://github.com/GradleUp/shadow/releases/tag/8.3.8) - 2025-07-01 + +**Fixed** + +- Fix the regression of `PropertiesFileTransformer` in `8.3.7`. ([#1493](https://github.com/GradleUp/shadow/pull/1493)) + +**Changed** + +- Expose Ant as `compile` scope. ([#1488](https://github.com/GradleUp/shadow/pull/1488)) + ## [8.3.7](https://github.com/GradleUp/shadow/releases/tag/8.3.7) - 2025-06-24 **Fixed** From a3482b69b2bbf266f7341791e73b4a4acb5f889a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 1 Jul 2025 21:57:05 +0800 Subject: [PATCH 513/941] Reflect the snapshot changes from central portal (#1426) * Update snapshot links https://central.sonatype.org/publish/publish-portal-snapshots/#consuming-via-gradle https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/com/gradleup/shadow/shadow-gradle-plugin/ * Apply suggestions from code review * Update README.md * Update docs/getting-started/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update docs/getting-started/README.md * Update docs/getting-started/README.md * Update changelog * Apply suggestions from code review * Remove unreached links for https://central.sonatype.com/service/rest/repository/browse/maven-snapshots/com/gradleup/shadow/shadow-gradle-plugin --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- README.md | 1 - docs/changes/README.md | 1 + docs/getting-started/README.md | 12 ++++-------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 89e94bbdd..f5d791223 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,6 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. ## Current Status [![Maven Central](https://img.shields.io/maven-central/v/com.gradleup.shadow/shadow-gradle-plugin)](https://central.sonatype.com/artifact/com.gradleup.shadow/shadow-gradle-plugin) -[![Sonatype Nexus (Snapshots)](https://img.shields.io/nexus/s/com.gradleup.shadow/shadow-gradle-plugin?&server=https://oss.sonatype.org/)](https://oss.sonatype.org/content/repositories/snapshots/com/gradleup/shadow/) [![Plugin Portal](https://img.shields.io/gradle-plugin-portal/v/com.gradleup.shadow)](https://plugins.gradle.org/plugin/com.gradleup.shadow) [![CI](https://github.com/GradleUp/shadow/actions/workflows/build.yml/badge.svg?branch=main&event=push)](https://github.com/GradleUp/shadow/actions/workflows/build.yml?query=branch:main+event:push) [![License](https://img.shields.io/github/license/GradleUp/shadow.svg)](LICENSE) diff --git a/docs/changes/README.md b/docs/changes/README.md index afa7fbea3..2c2fb974b 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -36,6 +36,7 @@ **Changed** - Update start script templates. ([#1419](https://github.com/GradleUp/shadow/pull/1419)) +- In-development snapshots are now published to the Central Portal Snapshots repository at https://central.sonatype.com/repository/maven-snapshots/. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) **Fixed** diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index d46311f61..3fabb03a0 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -57,9 +57,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: ===! "Kotlin"
- Snapshots of the development version are available in - - Sonatype's snapshots repository. + Snapshots of the development version are available in Central Portal Snapshots.

@@ -67,7 +65,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: buildscript { repositories { mavenCentral() - maven("https://oss.sonatype.org/content/repositories/snapshots/") + maven("https://central.sonatype.com/repository/maven-snapshots/") } dependencies { classpath("com.gradleup.shadow:shadow-gradle-plugin:") @@ -84,9 +82,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: === "Groovy"

- Snapshots of the development version are available in - - Sonatype's snapshots repository. + Snapshots of the development version are available in Central Portal Snapshots.

@@ -94,7 +90,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: buildscript { repositories { mavenCentral() - maven { url = 'https://oss.sonatype.org/content/repositories/snapshots/' } + maven { url = 'https://central.sonatype.com/repository/maven-snapshots/' } } dependencies { classpath 'com.gradleup.shadow:shadow-gradle-plugin:' From de8ecf5139089cafa688b300e9c546ce47fb84bd Mon Sep 17 00:00:00 2001 From: Jonathing Date: Tue, 1 Jul 2025 10:22:43 -0400 Subject: [PATCH 514/941] Don't re-add suppressed Gradle API to compileOnly configuration (#1422) As part of Gradle's experiments with publishing an external Gradle API (gradle/gradle issue 29483), they are beginning to look at suppressing the local Gradle API dependency that is usually used when the `java-gradle-plugin` plugin is applied. Because of this, though, the current strategy that the Shadow plugin uses to move it from `api` to `compileOnly` doesn't check if it exists to begin with. This PR aims to solve that problem. I've added tests as needed. Gradle's Plugin Publish plugin has a similar issue which I've gone into detail in gradle/plugin-portal-requests issue 260. * Add tests for checking presence of Gradle API in dependencies * Clean up tests * Less diff in configureJavaGradlePlugin * Remove afterEvaluate * Apply suggestions from code review * Update changelog * Try `named` * Revert "Try `named`" This reverts commit 0cae38effa42a55462bb8fe0c9673e9e840517ae. --------- Co-authored-by: Goooler --- docs/changes/README.md | 1 + .../gradle/plugins/shadow/JavaPluginTest.kt | 48 +++++++++++++++++++ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 13 ++--- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 2c2fb974b..a98082a2d 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -4,6 +4,7 @@ **Changed** +- Don't re-add suppressed Gradle API to `compileOnly` configuration. ([#1422](https://github.com/GradleUp/shadow/pull/1422)) - Bump the min Gradle requirement to 8.11. ([#1479](https://github.com/GradleUp/shadow/pull/1479)) - Expose Ant as `compile` scope. ([#1488](https://github.com/GradleUp/shadow/pull/1488)) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 2b116ba06..03337371d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -5,6 +5,7 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.containsMatch import assertk.assertions.containsOnly +import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import assertk.assertions.isFalse import assertk.assertions.isGreaterThan @@ -35,6 +36,8 @@ import kotlin.io.path.name import kotlin.io.path.outputStream import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.tasks.bundling.Jar import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Test @@ -489,6 +492,47 @@ class JavaPluginTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/1422", + ) + @Test + fun movesLocalGradleApiToCompileOnly() { + projectScriptPath.writeText( + """ + ${getDefaultProjectBuildScript("java-gradle-plugin")} + """.trimIndent() + System.lineSeparator(), + ) + + val outputCompileOnly = dependencies(COMPILE_ONLY_CONFIGURATION_NAME) + val outputApi = dependencies(API_CONFIGURATION_NAME) + + // "unspecified" is the local Gradle API. + assertThat(outputCompileOnly).contains("unspecified") + assertThat(outputApi).doesNotContain("unspecified") + } + + @Issue( + "https://github.com/GradleUp/shadow/issues/1422", + ) + @ParameterizedTest + @ValueSource(strings = [COMPILE_ONLY_CONFIGURATION_NAME, API_CONFIGURATION_NAME]) + fun doesNotReAddSuppressedGradleApi(configuration: String) { + projectScriptPath.writeText( + """ + ${getDefaultProjectBuildScript("java-gradle-plugin")} + """.trimIndent() + System.lineSeparator(), + ) + + val output = dependencies( + configuration = configuration, + // Internal flag added in 8.14 to experiment with suppressing local Gradle API. + "-Dorg.gradle.unsafe.suppress-gradle-api=true", + ) + + // "unspecified" is the local Gradle API. + assertThat(output).doesNotContain("unspecified") + } + @Issue( "https://github.com/GradleUp/shadow/issues/1070", ) @@ -734,4 +778,8 @@ class JavaPluginTest : BasePluginTest() { containsOnly(*entriesInAB, *manifestEntries) } } + + private fun dependencies(configuration: String, vararg flags: String): String { + return run("dependencies", "--configuration", configuration, *flags).output + } } 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 5ac21fdfe..003a30e6e 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 @@ -101,15 +101,16 @@ public abstract class ShadowJavaPlugin @Inject constructor( protected open fun Project.configureJavaGradlePlugin() { plugins.withType(JavaGradlePluginPlugin::class.java).configureEach { + val gradleApi = dependencies.gradleApi() // Remove the gradleApi so it isn't merged into the jar file. // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161. - configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { - it.dependencies.remove(dependencies.gradleApi()) - } - // Compile only gradleApi() to make sure the plugin can compile against Gradle API. - configurations.named(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) { - it.dependencies.add(dependencies.gradleApi()) + configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { api -> + // Only proceed if the removal is successful. + if (!api.dependencies.remove(gradleApi)) return@named + // Compile only gradleApi() to make sure the plugin can compile against Gradle API. + configurations.getByName(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) + .dependencies.add(dependencies.gradleApi()) } } } From 6013d44d633941e3e3965c149936df9863ac82b6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 2 Jul 2025 18:58:03 +0800 Subject: [PATCH 515/941] Prepare the release notes for 9.0.0-rc1 (#1496) --- docs/changes/README.md | 84 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index a98082a2d..1b368fb77 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,12 +2,94 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta17...HEAD) - 2025-xx-xx +> This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing +significant improvements to maintainability, performance, and future extensibility. It introduces many new features, +enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult +the [new doc site](https://gradleup.com/shadow/) before upgrading. + +**BREAKING** + +- Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) + Some APIs are marked as `internal`, and there are serial ABI changes. +- Remove Develocity integration. ([#1014](https://github.com/GradleUp/shadow/pull/1014)) +- Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) +- Migrate `ShadowJar` to using lazy properties. `isEnableRelocation` is removed, use `enableRelocation` instead. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) +- Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) +- Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as `SetProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) +- Migrate all `ListProperty` usages to `SetProperty`. Some public `List` parameters are also changed to `Set`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) +- Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) +- `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) +- Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) +- Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) +- Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) +- Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) +- Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) +- Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) +- Move `DependencyFilter` from `com.github.jengelman.gradle.plugins.shadow.internal` into `com.github.jengelman.gradle.plugins.shadow.tasks`. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) +- Rename `Transformer` to `ResourceTransformer`. Aims to better align with the name of `org.apache.maven.plugins.shade.resource.ResourceTransformer.java` and to distinguish itself from `org.gradle.api.Transformer.java`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) +- Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) +- Polish `ShadowSpec`. Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. `ShadowSpec` no longer extends `CopySpec`. Overload `relocate`, `transform` and things for better usability in Kotlin. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) +- Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) +- Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) +- Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip the files, only copy the files as-is. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + +**Added** + +- Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) +- Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) +- Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) +- Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) +- Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) +- Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) +- Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) +- Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) +- Compat Kotlin Multiplatform plugin. You still need to manually configure `manifest.attributes` (e.g. `Main-Class` attr) in the `shadowJar` task if necessary. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) +- 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 for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) +- Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) + **Changed** +- Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) +- Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) +- Support configuring `separator` in `AppendingTransformer`. This is useful for handling files like `resources/application.yml`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) +- Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) ([#1419](https://github.com/GradleUp/shadow/pull/1419)) +- Mark `ShadowJar.dependencyFilter` as `@Input`. `ShadowSpec.stats` is removed and `ShadowJar.stats` is `internal` for now. ([#1206](https://github.com/GradleUp/shadow/pull/1206)) +- Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) +- Polish `startShadowScripts` task registering. ([#1216](https://github.com/GradleUp/shadow/pull/1216)) +- Bump min Java requirement to 11. ([#1242](https://github.com/GradleUp/shadow/pull/1242)) +- Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) +- Migrate doc sites to MkDocs. ([#1302](https://github.com/GradleUp/shadow/pull/1302)) +- `runShadow` no longer depends on `installShadowDist`. ([#1353](https://github.com/GradleUp/shadow/pull/1353)) +- Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) +- Set `Main-Class` attr for KMP 1.9.0 or above. ([#1410](https://github.com/GradleUp/shadow/pull/1410)) +- In-development snapshots are now published to the Central Portal Snapshots repository at https://central.sonatype.com/repository/maven-snapshots/. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) +- Update ASM to 9.8 to support Java 25. ([#1380](https://github.com/GradleUp/shadow/pull/1380)) +- Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - Don't re-add suppressed Gradle API to `compileOnly` configuration. ([#1422](https://github.com/GradleUp/shadow/pull/1422)) - Bump the min Gradle requirement to 8.11. ([#1479](https://github.com/GradleUp/shadow/pull/1479)) - Expose Ant as `compile` scope. ([#1488](https://github.com/GradleUp/shadow/pull/1488)) +**Fixed** + +- Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) +- Adjust property initializations and modifiers in `ShadowJar`. This fixes the regression for registering custom `ShadowJar` tasks. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) +- Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) +- Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) +- Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) +- Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) +- Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) +- Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) +- Avoid creating jvm targets eagerly for KMP. ([#1378](https://github.com/GradleUp/shadow/pull/1378)) +- Allow using file trees of JARs together with the configuration cache. ([#1441](https://github.com/GradleUp/shadow/pull/1441)) +- Fallback `RelocateClassContext` and `RelocatePathContext` to data classes. ([#1445](https://github.com/GradleUp/shadow/pull/1445)) +- Pin the plugin's Kotlin language level on 2.0. The language level used in `9.0.0-beta14` is 2.2, which may cause compatibility issues for the plugins depending on Shadow. ([#1448](https://github.com/GradleUp/shadow/pull/1448)) +- Fix compatibility for Gradle 9.0.0 RC1. ([#1468](https://github.com/GradleUp/shadow/pull/1468)) +- Honor `DuplicatesStrategy`. Shadow recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. Now we honor `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + ## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18 **Fixed** @@ -641,7 +723,7 @@ type. - [Chris Rankin](https://github.com/chrisr3) - Add `ManifestAppenderTransformer` to support appending to Jar manifest [#474](https://github.com/GradleUp/shadow/pull/474) - [Min-Ken Lai](https://github.com/minkenlai) - Additional escaping fixes in start - script [#487](https://github.com/GradleUp/shadow/pull/487) + script [#487](httpshttps://github.com/GradleUp/shadow/pull/487) - [Alan D. Cabrera](https://github.com/maguro) - Automatically remove `gradleApi` from `compile` scope in the presence of `shadow` [#459](https://github.com/GradleUp/shadow/pull/459) - [Christian Stein](https://github.com/sormuras) - Do not initialize `UnusedTracker` when not From 52295cf28e26364e61d208d69e5d117a4950ae03 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 2 Jul 2025 18:59:28 +0800 Subject: [PATCH 516/941] Prepare version 9.0.0-rc1 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 1b368fb77..474f0f2c0 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,6 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-beta17...HEAD) - 2025-xx-xx +## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 > This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing significant improvements to maintainability, performance, and future extensibility. It introduces many new features, diff --git a/gradle.properties b/gradle.properties index dce25537f..7197916f8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-rc1 SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From 96b11b72ea5f13a01bec987e65e01523a7cdc4d6 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 2 Jul 2025 19:00:04 +0800 Subject: [PATCH 517/941] Prepare next development version --- docs/changes/README.md | 2 ++ gradle.properties | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 474f0f2c0..36e502285 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,5 +1,7 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc1...HEAD) - 2025-xx-xx + ## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 > This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing diff --git a/gradle.properties b/gradle.properties index 7197916f8..dce25537f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,7 +18,7 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-rc1 +VERSION_NAME=9.0.0-SNAPSHOT SONATYPE_AUTOMATIC_RELEASE=true SONATYPE_HOST=CENTRAL_PORTAL From 81cd0b72132ec5307ddf71d440a7f3846d74e917 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 2 Jul 2025 19:09:08 +0800 Subject: [PATCH 518/941] Fix the link for PR 487 --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 36e502285..15e7036f8 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -725,7 +725,7 @@ type. - [Chris Rankin](https://github.com/chrisr3) - Add `ManifestAppenderTransformer` to support appending to Jar manifest [#474](https://github.com/GradleUp/shadow/pull/474) - [Min-Ken Lai](https://github.com/minkenlai) - Additional escaping fixes in start - script [#487](httpshttps://github.com/GradleUp/shadow/pull/487) + script [#487](https://github.com/GradleUp/shadow/pull/487) - [Alan D. Cabrera](https://github.com/maguro) - Automatically remove `gradleApi` from `compile` scope in the presence of `shadow` [#459](https://github.com/GradleUp/shadow/pull/459) - [Christian Stein](https://github.com/sormuras) - Do not initialize `UnusedTracker` when not From aa5d30cba23dd262d580ecb567f3859dc3584bd6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Jul 2025 08:42:28 +0800 Subject: [PATCH 519/941] Update dependency org.junit:junit-bom to v5.13.3 (#1498) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 32c92c469..3a720d633 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -26,7 +26,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.6.0" -junit-bom = "org.junit:junit-bom:5.13.2" +junit-bom = "org.junit:junit-bom:5.13.3" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From b224199bfd97b937b1e965d43c71193bac9da360 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 6 Jul 2025 02:24:50 +0000 Subject: [PATCH 520/941] Document ResourceTransformer processing order guarantee (#1500) * Initial plan * Initial plan for ResourceTransformer processing order documentation Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> * Document ResourceTransformer processing order guarantee Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> * Add specific fabric.mod.json example addressing the original issue Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> * Revert tests * Correct the desc * Revert ResourceTransformer changes * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> Co-authored-by: Goooler --- docs/configuration/merging/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 412b350cb..aa1e13087 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -6,6 +6,18 @@ Maven Shade implementation. A [`ResourceTransformer`][ResourceTransformer] is in being written to the final output JAR. This allows a [`ResourceTransformer`][ResourceTransformer] to determine if it should process a particular entry and apply any modifications before writing the stream to the output. +**Important**: [`ResourceTransformer`][ResourceTransformer] follows a guaranteed processing order: + +1. **Project files first**: All files in projects are processed before any dependency files. +2. **Dependency files second**: Files from configurations (runtime dependencies) or added via [`ShadowJar.from`][ShadowJar.from] are processed after project files. + +This ordering is crucial when merging configuration files where you want to preserve project-specific values while +merging in additional data from dependencies. + +## Basic ResourceTransformer Usage + +For simpler use cases, you can create a basic transformer: + === "Kotlin" ```kotlin @@ -429,6 +441,7 @@ duplicated files. [ResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html [ServiceFileTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html [ShadowJar.append]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html +[ShadowJar.from]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action) [ShadowJar.transform]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html [ShadowJar]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html [XmlAppendingTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html From 6a587a0f4d5c29460f573ff99410ab6fa82787df Mon Sep 17 00:00:00 2001 From: Florian Dreier Date: Sun, 6 Jul 2025 11:22:22 +0200 Subject: [PATCH 521/941] Honor options.release for target JVM attribute (#1502) Co-authored-by: Zongle Wang --- docs/changes/README.md | 3 +++ .../gradle/plugins/shadow/PublishingTest.kt | 12 ++++++++++++ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 9 ++++++--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 15e7036f8..87634c053 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,6 +2,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc1...HEAD) - 2025-xx-xx +**Fixed** +- Honor `options.release` for target JVM attribute. ([#1502](https://github.com/GradleUp/shadow/pull/1502)) + ## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 > This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index afb861d75..0c740a109 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -87,6 +87,7 @@ class PublishingTest : BasePluginTest() { }.toTypedArray() val targetJvmAttr17 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "17" val targetJvmAttr11 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "11" + val targetJvmAttr8 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "8" projectScriptPath.appendText( """ @@ -118,6 +119,17 @@ class PublishingTest : BasePluginTest() { publish() // sourceCompatibility doesn't affect the target JVM version. assertions(attrsWithoutTargetJvm + targetJvmAttr11) + + projectScriptPath.appendText( + """ + tasks.named('compileJava') { + options.release = 8 + } + """.trimIndent() + System.lineSeparator(), + ) + publish() + // options.release flag is honored. + assertions(attrsWithoutTargetJvm + targetJvmAttr8) } @Test 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 003a30e6e..af026787a 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 @@ -75,9 +75,12 @@ public abstract class ShadowJavaPlugin @Inject constructor( objects.named(LibraryElements::class.java, LibraryElements.JAR), ) attr.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling::class.java, Bundling.SHADOWED)) - val targetJvmVersion = provider { - javaPluginExtension.targetCompatibility.majorVersion.toInt() - } + val targetJvmVersion = configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) + .map { compileClasspath -> + compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) + ?: javaPluginExtension.targetCompatibility.majorVersion.toInt() + } + // Track JavaPluginExtension to update targetJvmVersion when it changes. attr.attributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) } From 3b0b93d6b2247af159241d28abe5202bcfa0c774 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 6 Jul 2025 17:25:31 +0800 Subject: [PATCH 522/941] Import JavaPlugin constants --- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 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 af026787a..3c63d6e30 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 @@ -21,7 +21,9 @@ import org.gradle.api.attributes.Usage import org.gradle.api.attributes.java.TargetJvmVersion import org.gradle.api.component.AdhocComponentWithVariants import org.gradle.api.component.SoftwareComponentFactory -import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.language.base.plugins.LifecycleBasePlugin @@ -59,8 +61,8 @@ public abstract class ShadowJavaPlugin @Inject constructor( protected open fun Project.configureConfigurations() { val shadowConfiguration = configurations.shadow.get() - configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) { - it.extendsFrom(shadowConfiguration) + configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME) { compileClasspath -> + compileClasspath.extendsFrom(shadowConfiguration) } @Suppress("EagerGradleConfiguration") // this should be created eagerly. configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { @@ -75,7 +77,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( objects.named(LibraryElements::class.java, LibraryElements.JAR), ) attr.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling::class.java, Bundling.SHADOWED)) - val targetJvmVersion = configurations.named(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME) + val targetJvmVersion = configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME) .map { compileClasspath -> compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) ?: javaPluginExtension.targetCompatibility.majorVersion.toInt() @@ -108,11 +110,11 @@ public abstract class ShadowJavaPlugin @Inject constructor( // Remove the gradleApi so it isn't merged into the jar file. // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161. - configurations.named(JavaPlugin.API_CONFIGURATION_NAME) { api -> + configurations.named(API_CONFIGURATION_NAME) { api -> // Only proceed if the removal is successful. if (!api.dependencies.remove(gradleApi)) return@named // Compile only gradleApi() to make sure the plugin can compile against Gradle API. - configurations.getByName(JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME) + configurations.getByName(COMPILE_ONLY_CONFIGURATION_NAME) .dependencies.add(dependencies.gradleApi()) } } From 4f196a57bcbd99ef1d87345500cf160cfea5709c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 7 Jul 2025 23:33:32 +0000 Subject: [PATCH 523/941] Update plugin spotless to v7.1.0 (#1503) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3a720d633..c2d225ecf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,4 +35,4 @@ android-lint = "com.android.lint:8.11.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.33.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } -spotless = "com.diffplug.spotless:7.0.4" +spotless = "com.diffplug.spotless:7.1.0" From d0cf7323dab517231786738a9d03b727067b3ab1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 8 Jul 2025 17:56:43 +0800 Subject: [PATCH 524/941] Note the latest snapshot versions (#1504) --- docs/getting-started/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 3fabb03a0..b8f7f8bc7 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -68,6 +68,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: maven("https://central.sonatype.com/repository/maven-snapshots/") } dependencies { + // You can get the latest snapshot version from `VERSION_NAME` declared in https://github.com/GradleUp/shadow/blob/main/gradle.properties classpath("com.gradleup.shadow:shadow-gradle-plugin:") } } @@ -93,6 +94,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: maven { url = 'https://central.sonatype.com/repository/maven-snapshots/' } } dependencies { + // You can get the latest snapshot version from `VERSION_NAME` declared in https://github.com/GradleUp/shadow/blob/main/gradle.properties classpath 'com.gradleup.shadow:shadow-gradle-plugin:' } } From 0842360c3af8a73c0a20f666dc07fa784171f7c0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 08:59:51 +0800 Subject: [PATCH 525/941] Update plugin android-lint to v8.11.1 (#1506) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c2d225ecf..2c1f5add0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.11.0" +android-lint = "com.android.lint:8.11.1" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.33.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From 49d13efd81853bdd08406b370ae6e19ec81de6de Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 11 Jul 2025 09:02:47 +0800 Subject: [PATCH 526/941] Update start script templates to the latest (#1507) _Auto-generated by `Update Start Scripts` Github workflow._ Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> --- .../gradle/plugins/shadow/internal/unixStartScript.txt | 9 ++++++++- .../plugins/shadow/internal/windowsStartScript.txt | 4 +++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt index c71b056fc..1358dd7b4 100644 --- a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt +++ b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt @@ -147,7 +147,9 @@ case "\$( uname )" in #( NONSTOP* ) nonstop=true ;; esac +<% if ( classpath ) {%>\ CLASSPATH=$classpath +<% } %>\ <% if ( mainClassName.startsWith('--module ') ) { %> MODULE_PATH=$modulePath <% } %> @@ -207,7 +209,9 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "\$cygwin" || "\$msys" ; then APP_HOME=\$( cygpath --path --mixed "\$APP_HOME" ) +<% if ( classpath ) {%>\ CLASSPATH=\$( cygpath --path --mixed "\$CLASSPATH" ) +<% } %>\ <% if ( mainClassName.startsWith('--module ') ) { %> MODULE_PATH=\$( cygpath --path --mixed "\$MODULE_PATH" )<% } %> JAVACMD=\$( cygpath --unix "\$JAVACMD" ) @@ -251,7 +255,10 @@ DEFAULT_JVM_OPTS=${defaultJvmOpts} set -- \\ <% if ( appNameSystemProperty ) { %> "-D${appNameSystemProperty}=\$APP_BASE_NAME" \\ -<% } %> -classpath "\$CLASSPATH" \\ +<% } %>\ +<% if ( classpath ) {%>\ + -classpath "\$CLASSPATH" \\ +<% } %>\ <% if ( mainClassName.startsWith('--module ') ) { %> --module-path "\$MODULE_PATH" \\ <% } %> ${mainClassName ?: entryPointArgs} \\ diff --git a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt index 95b88fe56..82e7fa8a4 100644 --- a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt +++ b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt @@ -71,11 +71,13 @@ goto fail :execute @rem Setup the command line +<% if ( classpath ) {%>\ set CLASSPATH=$classpath +<% } %>\ <% if ( mainClassName.startsWith('--module ') ) { %>set MODULE_PATH=$modulePath<% } %> @rem Execute ${applicationName} -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %> -classpath "%CLASSPATH%" <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "%MODULE_PATH%" <% } %>${mainClassName ?: entryPointArgs} %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %><% if ( classpath ) {%> -classpath "%CLASSPATH%"<% } %> <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "%MODULE_PATH%" <% } %>${mainClassName ?: entryPointArgs} %* :end @rem End local scope for the variables with windows NT shell From 2ce8aee1bfd15db27d5ca4786c8b639f93b10688 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 11 Jul 2025 18:17:18 +0800 Subject: [PATCH 527/941] Update .editorconfig https://pinterest.github.io/ktlint/latest/rules/configuration-intellij-idea/#preventing-conflicts --- .editorconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index f36ddfdde..ca8471ed5 100755 --- a/.editorconfig +++ b/.editorconfig @@ -12,8 +12,8 @@ ij_kotlin_imports_layout = * ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = true ij_kotlin_line_break_after_multiline_when_entry = false -ij_kotlin_name_count_to_use_star_import = 999 -ij_kotlin_name_count_to_use_star_import_for_members = 999 +ij_kotlin_name_count_to_use_star_import = 2147483647 +ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 ij_kotlin_packages_to_use_import_on_demand = unset ktlint_code_style = intellij_idea ktlint_standard_function-expression-body = disabled From f0abbe201929b6c4a810cdb8b4b0112714dfae20 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 12 Jul 2025 06:46:51 +0800 Subject: [PATCH 528/941] Update dependency gradle to v9.0.0-rc-2 (#1508) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 407c905d9..4137226ae 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-rc-1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-rc-2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 9f6c3a12c32dcd7b7d0b3ce0b77120a93ffe14ea Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 12 Jul 2025 06:47:43 +0800 Subject: [PATCH 529/941] Update dependency org.apache.logging.log4j:log4j-core to v2.25.1 (#1509) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2c1f5add0..1d1738c8d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ pluginPublish = "1.3.1" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.19.0" -apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.0" +apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.1" apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.10" asm = "org.ow2.asm:asm-commons:9.8" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. From cab12ee509e067ced17e17125b8ece26603ee6be Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 14 Jul 2025 09:59:25 +0800 Subject: [PATCH 530/941] Replace maven-model-builder with maven-model --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1d1738c8d..bd8a4d602 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ pluginPublish = "1.3.1" apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.19.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.1" -apache-maven-modelBuilder = "org.apache.maven:maven-model-builder:3.9.10" +apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.10" asm = "org.ow2.asm:asm-commons:9.8" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.13" From c770645430bb0692daa440f3e56db2d7102c4ed9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 14 Jul 2025 10:38:25 +0800 Subject: [PATCH 531/941] Clean up publishing dependency checks (#1511) * Replace gav and gavs * Assert all scopes should be runtime * Tweak comment --- .../gradle/plugins/shadow/PublishingTest.kt | 27 +++++++++++-------- .../shadow/util/AppendableMavenRepository.kt | 6 +---- .../shadow/util/GradleModuleMetadata.kt | 3 +-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 0c740a109..cfdff4110 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -16,7 +16,7 @@ import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsNone import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import com.github.jengelman.gradle.plugins.shadow.util.gavs +import com.github.jengelman.gradle.plugins.shadow.util.coordinate import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi @@ -29,6 +29,7 @@ import kotlin.io.path.listDirectoryEntries import kotlin.io.path.name import kotlin.io.path.readText import kotlin.io.path.writeText +import org.apache.maven.model.Dependency import org.apache.maven.model.Model import org.apache.maven.model.io.xpp3.MavenXpp3Reader import org.gradle.api.JavaVersion @@ -361,11 +362,11 @@ class PublishingTest : BasePluginTest() { RUNTIME_ELEMENTS_CONFIGURATION_NAME, SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) - assertThat(gmm.runtimeElementsVariant.gavs).containsOnly( + assertThat(gmm.runtimeElementsVariant.coordinates).containsOnly( "example:client:1.0", ) - assertThat(gmm.shadowRuntimeElementsVariant.gavs).isEmpty() - assertShadowVariantCommon(gmm, gavs = emptyArray()) { + assertThat(gmm.shadowRuntimeElementsVariant.coordinates).isEmpty() + assertShadowVariantCommon(gmm, coordinates = emptyArray()) { transform { it.fileNames }.single().isEqualTo("server-1.0-all.jar") } } @@ -373,7 +374,7 @@ class PublishingTest : BasePluginTest() { assertThat(gmm.variantNames).containsOnly( SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) - assertShadowVariantCommon(gmm, gavs = emptyArray()) { + assertShadowVariantCommon(gmm, coordinates = emptyArray()) { transform { it.fileNames }.single().isEqualTo("client-1.0-all.jar") } } @@ -430,7 +431,7 @@ class PublishingTest : BasePluginTest() { Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_API, ) - transform { it.gavs }.isEmpty() + transform { it.coordinates }.isEmpty() } assertThat(gmm.runtimeElementsVariant).all { transform { it.attributes }.containsOnly( @@ -438,7 +439,7 @@ class PublishingTest : BasePluginTest() { Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, ) - transform { it.gavs }.containsOnly( + transform { it.coordinates }.containsOnly( "my:a:1.0", "my:b:1.0", ) @@ -512,20 +513,24 @@ class PublishingTest : BasePluginTest() { private fun assertPomCommon( pomPath: Path, - gavs: Array = arrayOf("my:b:1.0"), + coordinates: Array = arrayOf("my:b:1.0"), ) { - assertThat(pomReader.read(pomPath).gavs).containsOnly(*gavs) + assertThat(pomReader.read(pomPath)).all { + transform { it.dependencies.map(Dependency::coordinate) }.containsOnly(*coordinates) + // All scopes should be runtime. + transform { it.dependencies.map(Dependency::getScope).distinct() }.single().isEqualTo("runtime") + } } private fun assertShadowVariantCommon( gmm: GradleModuleMetadata, variantAttrs: Array> = shadowVariantAttrs, - gavs: Array = arrayOf("my:b:1.0"), + coordinates: Array = arrayOf("my:b:1.0"), body: Assert.() -> Unit = {}, ) { assertThat(gmm.shadowRuntimeElementsVariant).all { transform { it.attributes }.containsOnly(*variantAttrs) - transform { it.gavs }.containsOnly(*gavs) + transform { it.coordinates }.containsOnly(*coordinates) body() } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index 72bb30583..347ade283 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -11,7 +11,6 @@ import kotlin.io.path.name import kotlin.io.path.writeText import org.apache.maven.model.Dependency import org.apache.maven.model.Model -import org.apache.maven.model.ModelBase import org.gradle.testkit.runner.GradleRunner class AppendableMavenRepository( @@ -132,7 +131,4 @@ class AppendableMavenRepository( } } -val Dependency.gav: String get() = "$groupId:$artifactId:$version" - -@Suppress("SpellCheckingInspection") -val ModelBase.gavs: List get() = dependencies.map { it.gav } +val Dependency.coordinate: String get() = "$groupId:$artifactId:$version" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt index 08e305122..4ae3ebc55 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt @@ -22,8 +22,7 @@ data class GradleModuleMetadata( private val dependencies: List = emptyList(), private val files: List = emptyList(), ) { - @Suppress("SpellCheckingInspection") - val gavs: List get() = dependencies.map { it.coordinate } + val coordinates: List get() = dependencies.map { it.coordinate } val fileNames: List get() = files.map { it.name } data class Dependency( From 0cdcb9c6695d68e85ad13f4afd4cce6e36e587db Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 14 Jul 2025 16:11:28 +0800 Subject: [PATCH 532/941] Update plugin mavenPublish to v0.34.0 (#1512) * Update plugin mavenPublish to v0.34.0 * Updates https://github.com/vanniktech/gradle-maven-publish-plugin/releases/tag/0.34.0 --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- .github/workflows/build.yml | 3 +-- gradle.properties | 8 ++++---- gradle/libs.versions.toml | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 790fbacb6..b2f718652 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -67,8 +67,7 @@ jobs: - uses: gradle/actions/setup-gradle@v4 with: cache-read-only: true - # TODO: https://github.com/gradle/gradle/issues/22779 - - run: ./gradlew publishToMavenCentral --no-configuration-cache + - run: ./gradlew publishToMavenCentral env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_PORTAL_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }} diff --git a/gradle.properties b/gradle.properties index dce25537f..f4a1af2be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,14 +16,14 @@ org.jetbrains.dokka.experimental.tryK2.nowarn=true ########## Properties for publishing to Maven Central ########## +mavenCentralAutomaticPublishing=true +mavenCentralPublishing=true +signAllPublications=true + GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin VERSION_NAME=9.0.0-SNAPSHOT -SONATYPE_AUTOMATIC_RELEASE=true -SONATYPE_HOST=CENTRAL_PORTAL -RELEASE_SIGNING_ENABLED=true - POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. POM_INCEPTION_YEAR=2024 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bd8a4d602..d56caa234 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -33,6 +33,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.11.1" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" -mavenPublish = "com.vanniktech.maven.publish:0.33.0" +mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } spotless = "com.diffplug.spotless:7.1.0" From 4f02b00560637d6f89fd3914e85965e5458ded8d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 14 Jul 2025 16:57:24 +0800 Subject: [PATCH 533/941] Sign snapshots (#1513) --- .github/workflows/build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b2f718652..7a4b7fe0c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,6 +71,8 @@ jobs: env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_PORTAL_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }} + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} - uses: actions/upload-artifact@v4 with: path: build/libs From 6bce73c40210ab6f8856369aabe81745c8f8d6cf Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 14 Jul 2025 17:08:13 +0800 Subject: [PATCH 534/941] Explicit update-start-scripts workflow (#1514) There's no need to run the update task frequently; the start scripts are typically updated with Gradle releases. --- .github/workflows/build.yml | 23 --------------- .github/workflows/update-start-scripts.yml | 33 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/update-start-scripts.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a4b7fe0c..e69643f18 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,29 +31,6 @@ jobs: - uses: gradle/actions/setup-gradle@v4 - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" - # There's no need to run the update task frequently; the start scripts are typically updated with Gradle releases. - update-start-scripts: - runs-on: ubuntu-latest - if: github.event.repository.fork == false && github.ref == 'refs/heads/main' - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 - with: - distribution: 'zulu' - java-version: 21 - - uses: gradle/actions/setup-gradle@v4 - - run: ./gradlew downloadStartScripts - - uses: peter-evans/create-pull-request@v7 - with: - commit-message: | - Update start script templates to the latest - - _Auto-generated by `Update Start Scripts` Github workflow._ - title: Update start script templates to the latest - body: _Auto-generated by `Update Start Scripts` Github workflow._ - branch: actions/update-start-scripts - delete-branch: true - publish-snapshot: needs: build runs-on: ubuntu-latest diff --git a/.github/workflows/update-start-scripts.yml b/.github/workflows/update-start-scripts.yml new file mode 100644 index 000000000..d485c0abe --- /dev/null +++ b/.github/workflows/update-start-scripts.yml @@ -0,0 +1,33 @@ +name: Update Start Scripts + +on: + push: + branches: + - main + paths: + # There's no need to run the update task frequently; the start scripts are typically updated with Gradle releases. + - gradle/wrapper/** + workflow_dispatch: + +jobs: + update-start-scripts: + runs-on: ubuntu-latest + if: github.event.repository.fork == false + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + - uses: gradle/actions/setup-gradle@v4 + - run: ./gradlew downloadStartScripts + - uses: peter-evans/create-pull-request@v7 + with: + commit-message: | + Update start script templates to the latest + + _Auto-generated by `Update Start Scripts` Github workflow._ + title: Update start script templates to the latest + body: _Auto-generated by `Update Start Scripts` Github workflow._ + branch: actions/update-start-scripts + delete-branch: true From e6afa8e93dc278f9ec804b730a3e653abddbb335 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:10:42 +0800 Subject: [PATCH 535/941] Update dependency com.pinterest.ktlint:ktlint-cli to v1.7.0 (#1516) * Update dependency com.pinterest.ktlint:ktlint-cli to v1.7.0 * Fix style --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- gradle/libs.versions.toml | 2 +- .../gradle/plugins/shadow/PublishingTest.kt | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d56caa234..64d4e7f0f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.r androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. -ktlint = "com.pinterest.ktlint:ktlint-cli:1.6.0" +ktlint = "com.pinterest.ktlint:ktlint-cli:1.7.0" junit-bom = "org.junit:junit-bom:5.13.3" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index cfdff4110..79570aa1d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -65,7 +65,8 @@ class PublishingTest : BasePluginTest() { archiveClassifier = '' archiveBaseName = 'maven-all' """.trimIndent(), - ) + System.lineSeparator(), + ) + + System.lineSeparator(), ) publish() @@ -554,10 +555,11 @@ class PublishingTest : BasePluginTest() { TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to JavaVersion.current().majorVersion, ) - val shadowVariantAttrs = commonVariantAttrs + arrayOf( - Bundling.BUNDLING_ATTRIBUTE.name to Bundling.SHADOWED, - Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, - ) + val shadowVariantAttrs = commonVariantAttrs + + arrayOf( + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.SHADOWED, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + ) fun MavenXpp3Reader.read(path: Path): Model = path.inputStream().use { read(it) } From 5a5704995a62ab7e62f9a6ea1c56eaf945ee95f4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 15 Jul 2025 13:52:58 +0000 Subject: [PATCH 536/941] Update plugin com.gradle.develocity to v4.1 (#1517) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 0d136f4c5..ed3a041d3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "4.0.2" + id("com.gradle.develocity") version "4.1" } develocity { From 26846848c593b4b30e95b3315556b9ca72ddcba2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 16 Jul 2025 08:22:28 +0800 Subject: [PATCH 537/941] Update dependency org.apache.maven:maven-model to v3.9.11 (#1518) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 64d4e7f0f..406da55f3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ pluginPublish = "1.3.1" apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.19.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.1" -apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.10" +apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.11" asm = "org.ow2.asm:asm-commons:9.8" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.13" From b85b020900589752f3f98a52e350158e4002c131 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 17 Jul 2025 13:45:51 +0000 Subject: [PATCH 538/941] Update dependency gradle to v9.0.0-rc-3 (#1520) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 4137226ae..79ce75b0c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-rc-2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-rc-3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 4281b73012b93310498849591de83268017012d5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 17 Jul 2025 22:02:08 +0800 Subject: [PATCH 539/941] Add testGradleVersion into buildScan values (#1521) --- build.gradle.kts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 4eb0faa95..e79385c6d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -96,6 +96,16 @@ publishing.publications.withType().configureEach { suppressPomMetadataWarningsFor(SOURCES_ELEMENTS_CONFIGURATION_NAME) } +val testGradleVersion: String = providers.gradleProperty("testGradleVersion").orNull.let { + val value = if (it == null || it == "current") GradleVersion.current().version else it + logger.info("Using test Gradle version: $value") + value +} + +develocity { + buildScan.value("testGradleVersion", testGradleVersion) +} + dependencies { compileOnly(libs.kotlin.kmp) api(libs.apache.ant) // Types from Ant are exposed in the public API. @@ -167,12 +177,7 @@ testing.suites { } targets.configureEach { testTask { - val testGradleVersion = providers.gradleProperty("testGradleVersion").orNull.let { - if (it == null || it == "current") GradleVersion.current().version else it - } - logger.info("Using test Gradle version: $testGradleVersion") systemProperty("TEST_GRADLE_VERSION", testGradleVersion) - maxParallelForks = Runtime.getRuntime().availableProcessors() } } From 33ce76f4c9328473217415f17883a3a9ddf22c70 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 18 Jul 2025 23:24:43 +0000 Subject: [PATCH 540/941] Update dependency commons-io:commons-io to v2.20.0 (#1523) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 406da55f3..47cba86e6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,7 +5,7 @@ pluginPublish = "1.3.1" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" -apache-commonsIo = "commons-io:commons-io:2.19.0" +apache-commonsIo = "commons-io:commons-io:2.20.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.1" apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.11" asm = "org.ow2.asm:asm-commons:9.8" From 0511272ebf38c74e647b613b94497b04495c10d2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 19 Jul 2025 17:45:53 +0800 Subject: [PATCH 541/941] Let assemble depend on shadowJar (#1524) * Update `applyPlugin` and `registerShadowJarCommon` * Update changelog * Suppress `EagerGradleConfiguration` * Fix doc tests --- docs/changes/README.md | 5 +++++ docs/configuration/merging/README.md | 8 ++++---- docs/publishing/README.md | 19 +++++++++++-------- .../shadow/snippet/SnippetExecutable.kt | 11 +++++++++++ .../gradle/plugins/shadow/JavaPluginTest.kt | 3 +++ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 2 ++ 6 files changed, 36 insertions(+), 12 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 87634c053..bbc50ecf2 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -2,7 +2,12 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc1...HEAD) - 2025-xx-xx +**Added** + +- Let `assemble` depend on `shadowJar`. ([#1524](https://github.com/GradleUp/shadow/pull/1524)) + **Fixed** + - Honor `options.release` for target JVM attribute. ([#1502](https://github.com/GradleUp/shadow/pull/1502)) ## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index aa1e13087..74f37dfc9 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -69,7 +69,7 @@ Additionally, a [`ResourceTransformer`][ResourceTransformer] can accept a `Closu import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement - class MyTransformer(var enabled: Boolean = false) : ResourceTransformer { + class MyTransformer(@get:Input var enabled: Boolean = false) : ResourceTransformer { override fun canTransformResource(element: FileTreeElement): Boolean = true override fun transform(context: TransformerContext) {} override fun hasTransformedResource(): Boolean = true @@ -92,7 +92,7 @@ Additionally, a [`ResourceTransformer`][ResourceTransformer] can accept a `Closu import org.gradle.api.file.FileTreeElement class MyTransformer implements ResourceTransformer { - boolean enabled + @Input boolean enabled @Override boolean canTransformResource(FileTreeElement element) { return true } @Override void transform(TransformerContext context) {} @Override boolean hasTransformedResource() { return true } @@ -116,7 +116,7 @@ An instantiated instance of a [`ResourceTransformer`][ResourceTransformer] can a import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement - class MyTransformer(val enabled: Boolean) : ResourceTransformer { + class MyTransformer(@get:Input val enabled: Boolean) : ResourceTransformer { override fun canTransformResource(element: FileTreeElement): Boolean = true override fun transform(context: TransformerContext) {} override fun hasTransformedResource(): Boolean = true @@ -137,7 +137,7 @@ An instantiated instance of a [`ResourceTransformer`][ResourceTransformer] can a import org.gradle.api.file.FileTreeElement class MyTransformer implements ResourceTransformer { - final boolean enabled + @Input final boolean enabled MyTransformer(boolean enabled) { this.enabled = enabled } @Override boolean canTransformResource(FileTreeElement element) { return true } @Override void transform(TransformerContext context) {} diff --git a/docs/publishing/README.md b/docs/publishing/README.md index c7708c917..74c37ab7c 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -85,12 +85,14 @@ the `archiveClassifier` of the shadowed JAR like the following: version = "1.0" dependencies { + val retrofitVersion = "2.12.0" // This will be bundled in the shadowed JAR and not declared in the POM. - implementation("some:a:1.0") - // This will be excluded - shadow("some:b:1.0") - // This will be excluded - compileOnly("some:c:1.0") + implementation("com.squareup.retrofit2:retrofit:$retrofitVersion") + // This will be excluded from the shadowed JAR but declared as a runtime dependency in `META-INF/MANIFEST.MF` + // file's `Class-Path` entry, and also in the POM file. + shadow("com.squareup.retrofit2:converter-java8:$retrofitVersion") + // This will be excluded from the shadowed JAR and not declared in the POM or `META-INF/MANIFEST.MF`. + compileOnly("com.squareup.retrofit2:converter-scalars:$retrofitVersion") } tasks.shadowJar { @@ -122,13 +124,14 @@ the `archiveClassifier` of the shadowed JAR like the following: version = '1.0' dependencies { + def retrofitVersion = '2.12.0' // This will be bundled in the shadowed JAR and not declared in the POM. - implementation 'some:a:1.0' + implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" // This will be excluded from the shadowed JAR but declared as a runtime dependency in `META-INF/MANIFEST.MF` // file's `Class-Path` entry, and also in the POM file. - shadow 'some:b:1.0' + shadow "com.squareup.retrofit2:converter-java8:$retrofitVersion" // This will be excluded from the shadowed JAR and not declared in the POM or `META-INF/MANIFEST.MF`. - compileOnly 'some:c:1.0' + compileOnly "com.squareup.retrofit2:converter-scalars:$retrofitVersion" } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index df3d25ef7..c8d3434ec 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -2,7 +2,10 @@ package com.github.jengelman.gradle.plugins.shadow.snippet import java.lang.System.lineSeparator import java.nio.file.Path +import java.util.jar.JarOutputStream import kotlin.io.path.createDirectory +import kotlin.io.path.createFile +import kotlin.io.path.outputStream import kotlin.io.path.writeText import org.gradle.testkit.runner.GradleRunner import org.junit.jupiter.api.function.Executable @@ -62,6 +65,14 @@ sealed class SnippetExecutable : Executable { append(lineSeparator()) }.trimIndent() projectRoot.addSubProject("main", mainScript) + projectRoot.resolve("main/foo.jar").createFile().also { + // Dummy JAR file to ensure the project can be built. + JarOutputStream(it.outputStream()).use {} + } + projectRoot.resolve("main/bar.jar").createFile().also { + // Dummy JAR file to ensure the project can be built. + JarOutputStream(it.outputStream()).use {} + } try { GradleRunner.create() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 03337371d..993d33cf9 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -39,6 +39,7 @@ import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.tasks.bundling.Jar +import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest @@ -62,6 +63,8 @@ class JavaPluginTest : BasePluginTest() { project.plugins.apply(JavaPlugin::class.java) val shadowTask = project.tasks.getByName(SHADOW_JAR_TASK_NAME) as ShadowJar val shadowConfig = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) + val assembleTask = project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME) + assertThat(assembleTask.dependsOn).contains(shadowTask) // Check extended properties. with(shadowTask as Jar) { 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 3c63d6e30..860ec6a7d 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 @@ -147,6 +147,8 @@ public abstract class ShadowJavaPlugin @Inject constructor( "META-INF/versions/**/module-info.class", "module-info.class", ) + @Suppress("EagerGradleConfiguration") // Can't use `named` as the task is optional. + tasks.findByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME)?.dependsOn(task) action.execute(task) } } From edcb14397dd74550646670d0c15ff94ca1240131 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 19 Jul 2025 18:16:11 +0800 Subject: [PATCH 542/941] Disable lint tasks on Windows (#1526) ``` Failed A failure occurred while executing com.android.build.gradle.internal.lint.AndroidLintTask$AndroidLintLauncherWorkAction > There was a failure while executing work items > A failure occurred while executing com.android.build.gradle.internal.lint.AndroidLintWorkAction > java.lang.ExceptionInInitializerError (no error message) ... Caused by: java.lang.RuntimeException: Could not find installation home path. Please make sure product-info.json is present in the installation directory. at com.intellij.openapi.application.PathManager.getHomePath(PathManager.java:113) at com.intellij.openapi.application.PathManager.getHomePath(PathManager.java:82) at com.intellij.openapi.application.PathManager.getConfigPath(PathManager.java:368) at com.intellij.openapi.application.PathManager.getConfigDir(PathManager.java:356) at com.intellij.openapi.util.registry.EarlyAccessRegistryManagerKt.configFile_delegate$lambda$0(EarlyAccessRegistryManager.kt:27) at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:83) at com.intellij.openapi.util.registry.EarlyAccessRegistryManagerKt.getConfigFile(EarlyAccessRegistryManager.kt:26) at com.intellij.openapi.util.registry.EarlyAccessRegistryManagerKt.lazyMap$lambda$2(EarlyAccessRegistryManager.kt:33) at com.intellij.util.concurrency.SynchronizedClearableLazy._get_value_$lambda$1$lambda$0(SynchronizedClearableLazy.kt:41) at com.intellij.util.concurrency.SynchronizedClearableLazy.getValue(SynchronizedClearableLazy.kt:40) at com.intellij.openapi.util.registry.EarlyAccessRegistryManager.getString(EarlyAccessRegistryManager.kt:84) at com.intellij.openapi.util.registry.EarlyAccessRegistryManager.getBoolean(EarlyAccessRegistryManager.kt:75) at com.intellij.ide.plugins.DisabledPluginsState.(DisabledPluginsState.kt:32) at com.intellij.ide.plugins.PluginEnabler.(PluginEnabler.java:22) at com.intellij.core.CoreApplicationEnvironment.(CoreApplicationEnvironment.java:84) at com.intellij.core.JavaCoreApplicationEnvironment.(JavaCoreApplicationEnvironment.java:47) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment.(KotlinCoreApplicationEnvironment.kt:44) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment.(KotlinCoreApplicationEnvironment.kt) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreApplicationEnvironment$Companion.create(KotlinCoreApplicationEnvironment.kt:113) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$Companion.createApplicationEnvironment(KotlinCoreEnvironment.kt:678) at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$Companion.getOrCreateApplicationEnvironment(KotlinCoreEnvironment.kt:563) at org.jetbrains.kotlin.analysis.api.standalone.base.projectStructure.StandaloneProjectFactory.createProjectEnvironment(StandaloneProjectFactory.kt:81) at org.jetbrains.kotlin.analysis.api.standalone.StandaloneAnalysisAPISessionBuilder.(StandaloneAnalysisAPISessionBuilder.kt:74) at com.android.tools.lint.FirUastEnvironmentKt.createAnalysisSession(FirUastEnvironment.kt:210) at com.android.tools.lint.FirUastEnvironmentKt.access$createAnalysisSession(FirUastEnvironment.kt:1) at com.android.tools.lint.FirUastEnvironment$Companion.create(FirUastEnvironment.kt:92) at com.android.tools.lint.UastEnvironment$Companion.create(UastEnvironment.kt:173) at com.android.tools.lint.LintCliClient.initializeProjects(LintCliClient.kt:1411) at com.android.tools.lint.client.api.LintClient.performInitializeProjects$lint_api(LintClient.kt:1011) at com.android.tools.lint.client.api.LintDriver.initializeProjectRoots(LintDriver.kt:570) at com.android.tools.lint.client.api.LintDriver.doAnalyze(LintDriver.kt:485) at com.android.tools.lint.client.api.LintDriver.doAnalyze$default(LintDriver.kt:479) at com.android.tools.lint.client.api.LintDriver.mergeOnly(LintDriver.kt:465) at com.android.tools.lint.LintCliClient$mergeOnly$1.invoke(LintCliClient.kt:302) at com.android.tools.lint.LintCliClient$mergeOnly$1.invoke(LintCliClient.kt:299) at com.android.tools.lint.LintCliClient.run(LintCliClient.kt:330) at com.android.tools.lint.LintCliClient.mergeOnly(LintCliClient.kt:299) at com.android.tools.lint.Main.run(Main.java:1775) at com.android.tools.lint.Main.run(Main.java:283) at com.android.build.gradle.internal.lint.AndroidLintWorkAction.invokeLintMainRunMethod(AndroidLintWorkAction.kt:103) at com.android.build.gradle.internal.lint.AndroidLintWorkAction.runLint(AndroidLintWorkAction.kt:90) at com.android.build.gradle.internal.lint.AndroidLintWorkAction.execute(AndroidLintWorkAction.kt:64) at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:68) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66) at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62) at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100) at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62) at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44) at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209) at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204) at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66) at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166) at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59) at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53) at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41) at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59) at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:176) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169) at org.gradle.internal.Factories$1.create(Factories.java:31) at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263) at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127) at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164) at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133) at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64) at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47) ``` --- build.gradle.kts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index e79385c6d..2993c9767 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME -import org.gradle.kotlin.dsl.kotlin +import org.jetbrains.kotlin.daemon.common.OSKind import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion @@ -229,6 +229,13 @@ tasks.validatePlugins { enableStricterValidation = true } +tasks.whenTaskAdded { + if (name.contains("lint") && this::class.java.name.contains("com.android.build")) { + // Disable lint tasks for Windows due to ExceptionInInitializerError. + enabled = OSKind.current != OSKind.Windows + } +} + tasks.check { dependsOn( tasks.withType(), From 324c60e03f9d87262a4ec4fb1fe63800d78c54de Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 19 Jul 2025 18:23:40 +0800 Subject: [PATCH 543/941] Remove outdated incompatibleWithLowerMinGradleVersion (#1525) --- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 993d33cf9..71b69c5bf 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -96,13 +96,6 @@ class JavaPluginTest : BasePluginTest() { assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile) } - @Test - fun incompatibleWithLowerMinGradleVersion() { - runWithFailure(shadowJarTask) { - it.withGradleVersion("8.2") - } - } - @Test fun shadowJarCliOptions() { val result = run("help", "--task", shadowJarTask) From 31f504f6b0ec7512ad3a637e939e78da4a6f91e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 07:09:08 +0800 Subject: [PATCH 544/941] Update plugin spotless to v7.2.0 (#1528) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 47cba86e6..0a2fcf012 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,4 +35,4 @@ android-lint = "com.android.lint:8.11.1" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } -spotless = "com.diffplug.spotless:7.1.0" +spotless = "com.diffplug.spotless:7.2.0" From 1e76f6fd6527b2e1f9af2ba570c0691dae5ca7ff Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 21 Jul 2025 14:07:22 +0800 Subject: [PATCH 545/941] Fail build when inputting AAR files or using Shadow with AGP (#1530) * Throw exceptions if mixed Shadow with AGP * Downgrade AGP * Fail build if inputting AAR files * Update changelog * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Remove unnecessary setup --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/renovate.json5 | 11 +++++-- build.gradle.kts | 1 + docs/changes/README.md | 1 + gradle/libs.versions.toml | 3 ++ .../plugins/shadow/AndroidPluginTest.kt | 29 +++++++++++++++++++ .../gradle/plugins/shadow/JavaPluginTest.kt | 20 +++++++++++++ .../gradle/plugins/shadow/ShadowPlugin.kt | 6 ++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 14 ++++++++- 8 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 91920c870..5b51e7a02 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -13,7 +13,14 @@ "matchPackageNames": [ "org.vafer:jdependency", "org.ow2.asm:asm-commons" - ] - } + ], + }, + { + // AGP version should match the min Gradle version used in tests. + "enabled": false, + "matchPackageNames": [ + 'com.android.tools.build:gradle', + ], + }, ] } diff --git a/build.gradle.kts b/build.gradle.kts index 2993c9767..b2a6fc6c3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -117,6 +117,7 @@ dependencies { implementation(libs.plexus.utils) implementation(libs.plexus.xml) + testPluginClasspath(libs.agp) testPluginClasspath(libs.foojayResolver) testPluginClasspath(libs.kotlin.kmp) testPluginClasspath(libs.pluginPublish) diff --git a/docs/changes/README.md b/docs/changes/README.md index bbc50ecf2..b9ea958ca 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -5,6 +5,7 @@ **Added** - Let `assemble` depend on `shadowJar`. ([#1524](https://github.com/GradleUp/shadow/pull/1524)) +- Fail build when inputting AAR files or using Shadow with AGP. ([#1530](https://github.com/GradleUp/shadow/pull/1530)) **Fixed** diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a2fcf012..bb9f25687 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,6 +21,9 @@ moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "mosh foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:1.0.0" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } +# AGP version should match the min Gradle version used in tests. +# https://developer.android.com/build/releases/gradle-plugin#updating-gradle +agp = "com.android.tools.build:gradle:8.8.0" androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt new file mode 100644 index 000000000..6cce09a7b --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt @@ -0,0 +1,29 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.assertThat +import assertk.assertions.contains +import kotlin.io.path.writeText +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +class AndroidPluginTest : BasePluginTest() { + @ParameterizedTest + @MethodSource("androidIdsProvider") + fun doesNotCompatAgp(pluginId: String) { + projectScriptPath.writeText(getDefaultProjectBuildScript(plugin = pluginId)) + + assertThat(runWithFailure().output).contains( + "Shadow does not support using with AGP, you may need Android Fused Library plugin instead.", + ) + } + + private companion object { + @JvmStatic + fun androidIdsProvider() = listOf( + "com.android.application", + "com.android.library", + "com.android.test", + "com.android.dynamic-feature", + ) + } +} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 71b69c5bf..b205fa180 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -596,6 +596,26 @@ class JavaPluginTest : BasePluginTest() { assertThat(result.output).containsMatch("Cannot expand ZIP '.*bad\\.jar'".toRegex()) } + @Test + fun failBuildIfProcessingAar() { + val fooAarPath = path("foo.aar") + + projectScriptPath.appendText( + """ + dependencies { + ${implementationFiles(fooAarPath)} + } + """.trimIndent(), + ) + + val result = runWithFailure(shadowJarTask) + + assertThat(result.output).contains( + "Shadowing AAR file is not supported.", + "Please exclude dependency artifact:", + ) + } + @Test fun worksWithArchiveFileName() { val mainClassEntry = writeClass() 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 b35c5dc27..1f0b52a8f 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 @@ -22,6 +22,12 @@ public abstract class ShadowPlugin : Plugin { withId(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { apply(ShadowKmpPlugin::class.java) } + withId("com.android.base") { + error( + "Shadow does not support using with AGP, you may need Android Fused Library plugin instead. " + + "See https://developer.android.com/build/publish-library/fused-library", + ) + } // Apply the legacy plugin last. // Because we apply the ShadowJavaPlugin/ShadowApplication plugin in a withType callback for the diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 28422774c..b63133c4e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -311,7 +311,19 @@ public abstract class ShadowJar : @TaskAction override fun copy() { - from(includedDependencies.files.map { archiveOperations.zipTree(it) }) + from( + includedDependencies.files.map { file -> + if (file.extension.equals("aar", ignoreCase = true)) { + val message = """ + Shadowing AAR file is not supported. + Please exclude dependency artifact: $file + or use Android Fused Library plugin instead. See https://developer.android.com/build/publish-library/fused-library. + """.trimIndent() + error(message) + } + archiveOperations.zipTree(file) + }, + ) injectMultiReleaseAttrIfPresent() super.copy() } From f8cfbe1d798e114348afc00c1cb3136e8ffae76e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 21 Jul 2025 12:00:02 +0000 Subject: [PATCH 546/941] Update dependency org.junit:junit-bom to v5.13.4 (#1531) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bb9f25687..db603e803 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.7.0" -junit-bom = "org.junit:junit-bom:5.13.3" +junit-bom = "org.junit:junit-bom:5.13.4" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From d48efedb456ede47fc3efde1bd1f851ac0e3be84 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:51:14 +0800 Subject: [PATCH 547/941] Update dependency com.pinterest.ktlint:ktlint-cli to v1.7.1 (#1532) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index db603e803..45b938cd2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,7 +27,7 @@ agp = "com.android.tools.build:gradle:8.8.0" androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. -ktlint = "com.pinterest.ktlint:ktlint-cli:1.7.0" +ktlint = "com.pinterest.ktlint:ktlint-cli:1.7.1" junit-bom = "org.junit:junit-bom:5.13.4" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" From e8e8a4921efd75f5ccb7a8d3318e337a1a9bdaae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 22 Jul 2025 09:51:35 +0800 Subject: [PATCH 548/941] Update plugin spotless to v7.2.1 (#1533) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 45b938cd2..ba42ffbe9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,4 +38,4 @@ android-lint = "com.android.lint:8.11.1" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } -spotless = "com.diffplug.spotless:7.2.0" +spotless = "com.diffplug.spotless:7.2.1" From c2d90bceb7716451be3de70c6d7c3cdbfeeed6b6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 22 Jul 2025 18:35:42 +0800 Subject: [PATCH 549/941] Test relocation for case-sensitive and case-insensitive classes (#1535) * Test `relocateCaseSensitiveAndInsensitiveClasses` * Update src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt * Update src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt * Enable it for Linux only --- .../gradle/plugins/shadow/RelocationTest.kt | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 083d955ce..15ba3b43b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -18,6 +18,8 @@ import kotlin.io.path.appendText import kotlin.io.path.writeText import kotlin.time.Duration.Companion.seconds import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.EnabledOnOs +import org.junit.jupiter.api.condition.OS import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -614,6 +616,35 @@ class RelocationTest : BasePluginTest() { ) } + @EnabledOnOs( + value = [OS.LINUX], + disabledReason = "Mac/Windows file system is case insensitive, so this test is not applicable.", + ) + @Test + fun relocateCaseSensitiveAndInsensitiveClasses() { + val fooClass1 = writeClass(className = "foo") + val fooClass2 = writeClass(className = "Foo") + projectScriptPath.appendText( + """ + $shadowJar { + relocate('my', 'bar.my') + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsOnly( + "bar/", + "bar/my/", + "bar/$fooClass1", + "bar/$fooClass2", + *manifestEntries, + ) + } + } + private companion object { @JvmStatic fun prefixProvider() = listOf( From 81bb2abe14fdbbc4afa22e83a8deac232d83217e Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 22 Jul 2025 18:48:51 +0800 Subject: [PATCH 550/941] Revert "Test relocation for case-sensitive and case-insensitive classes (#1535)" This reverts commit c2d90bceb7716451be3de70c6d7c3cdbfeeed6b6. --- .../gradle/plugins/shadow/RelocationTest.kt | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 15ba3b43b..083d955ce 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -18,8 +18,6 @@ import kotlin.io.path.appendText import kotlin.io.path.writeText import kotlin.time.Duration.Companion.seconds import org.junit.jupiter.api.Test -import org.junit.jupiter.api.condition.EnabledOnOs -import org.junit.jupiter.api.condition.OS import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -616,35 +614,6 @@ class RelocationTest : BasePluginTest() { ) } - @EnabledOnOs( - value = [OS.LINUX], - disabledReason = "Mac/Windows file system is case insensitive, so this test is not applicable.", - ) - @Test - fun relocateCaseSensitiveAndInsensitiveClasses() { - val fooClass1 = writeClass(className = "foo") - val fooClass2 = writeClass(className = "Foo") - projectScriptPath.appendText( - """ - $shadowJar { - relocate('my', 'bar.my') - } - """.trimIndent(), - ) - - run(shadowJarTask) - - assertThat(outputShadowJar).useAll { - containsOnly( - "bar/", - "bar/my/", - "bar/$fooClass1", - "bar/$fooClass2", - *manifestEntries, - ) - } - } - private companion object { @JvmStatic fun prefixProvider() = listOf( From 9ada0afe7bafd00df68f286978be09daf82a750f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 23 Jul 2025 10:40:37 +0800 Subject: [PATCH 551/941] Minor tweaks (#1536) * Tweak comments * Revert "Update dependency com.pinterest.ktlint:ktlint-cli to v1.7.0 (#1516)" This reverts commit e6afa8e9 * Rearrange * Tweak hasTransformedResource styles * Tweak AppendingTransformer styles * Cleanups --- .../gradle/plugins/shadow/PublishingTest.kt | 12 +++---- .../plugins/shadow/internal/GradleCompat.kt | 6 ++-- .../shadow/internal/RelocatorRemapper.kt | 2 +- .../shadow/relocation/SimpleRelocator.kt | 2 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 5 --- .../transformers/AppendingTransformer.kt | 30 ++++++++--------- .../ComponentsXmlResourceTransformer.kt | 32 +++++++++---------- .../GroovyExtensionModuleTransformer.kt | 1 + .../Log4j2PluginsCacheFileTransformer.kt | 4 +-- .../transformers/PropertiesFileTransformer.kt | 4 +-- .../shadow/relocation/SimpleRelocatorTest.kt | 14 ++++---- 11 files changed, 49 insertions(+), 63 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 79570aa1d..cfdff4110 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -65,8 +65,7 @@ class PublishingTest : BasePluginTest() { archiveClassifier = '' archiveBaseName = 'maven-all' """.trimIndent(), - ) + - System.lineSeparator(), + ) + System.lineSeparator(), ) publish() @@ -555,11 +554,10 @@ class PublishingTest : BasePluginTest() { TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to JavaVersion.current().majorVersion, ) - val shadowVariantAttrs = commonVariantAttrs + - arrayOf( - Bundling.BUNDLING_ATTRIBUTE.name to Bundling.SHADOWED, - Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, - ) + val shadowVariantAttrs = commonVariantAttrs + arrayOf( + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.SHADOWED, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + ) fun MavenXpp3Reader.read(path: Path): Model = path.inputStream().use { read(it) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index 170668b21..262b95ab2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -22,10 +22,8 @@ import org.gradle.jvm.toolchain.JavaToolchainService * Return `runtimeClasspath` or `runtime` configuration. */ internal inline val Project.runtimeConfiguration: Configuration - get() { - return configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) - ?: configurations.getByName("runtime") - } + get() = configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) + ?: configurations.getByName("runtime") internal inline val Project.sourceSets: SourceSetContainer get() = extensions.getByType(SourceSetContainer::class.java) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 42c0e69d1..d362996b1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -49,6 +49,6 @@ internal class RelocatorRemapper( } fun mapPath(path: String): String { - return map(path.substring(0, path.indexOf('.'))) + return map(path.substringBefore('.')) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index c5ee0e436..ad2d7d0e5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -212,7 +212,7 @@ public open class SimpleRelocator @JvmOverloads constructor( // Actually, class patterns should just use 'foo.bar.*' ending with a single asterisk, but some users // mistake them for path patterns like 'my/path/**', so let us be a bit more lenient here. if (filePattern.endsWith("/*") || filePattern.endsWith("/**")) { - val packagePattern = filePattern.substring(0, filePattern.lastIndexOf('/')) + val packagePattern = filePattern.take(filePattern.lastIndexOf('/')) add(packagePattern) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 9eb262ffd..c498e9e87 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -87,9 +87,6 @@ public open class ShadowCopyAction( } } - /** - * Handles cases like [issue 53](https://github.com/GradleUp/shadow/issues/53). - */ private fun addDirs(zos: ZipOutputStream) { @Suppress("UNCHECKED_CAST") val entries = zos::class.java.getDeclaredField("entries").apply { isAccessible = true } @@ -173,8 +170,6 @@ public open class ShadowCopyAction( /** * Applies remapping to the given class with the specified relocation path. The remapped class is then written * to the zip file. - * - * See [issue 364](https://github.com/GradleUp/shadow/issues/364) and [issue 408](https://github.com/GradleUp/shadow/issues/408). */ private fun FileCopyDetails.remapClass() = file.inputStream().use { inputStream -> // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index 878e44ab3..5fda7760f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -19,14 +19,11 @@ import org.gradle.api.tasks.Optional * @author John Engelman */ @CacheableTransformer -@Suppress("ktlint:standard:backing-property-naming") public open class AppendingTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : ResourceTransformer { - /** - * Defer initialization, see [issue 763](https://github.com/GradleUp/shadow/issues/763). - */ - private var _data: ByteArrayOutputStream? = null + @Suppress("ktlint:standard:backing-property-naming") + private var _data: ByteArrayOutputStream? = null // It's nullable to allow lazy initialization to support CC. private inline val data get() = _data ?: ByteArrayOutputStream().also { _data = it } @get:Optional @@ -41,23 +38,24 @@ public open class AppendingTransformer @Inject constructor( } override fun transform(context: TransformerContext) { - if (data.size() > 0) { - // Append the separator before the new content to ensure the separator is not at the end of the file. - data.write(separator.get().toByteArray()) + data.let { + if (it.size() > 0) { + // Append the separator before the new content to ensure the separator is not at the end of the file. + it.write(separator.get().toByteArray()) + } + context.inputStream.copyTo(it) } - context.inputStream.copyTo(data) } - override fun hasTransformedResource(): Boolean { - return data.size() > 0 - } + override fun hasTransformedResource(): Boolean = data.size() > 0 override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { os.putNextEntry(zipEntry(resource.get(), preserveFileTimestamps)) - - // Closing a ByteArrayOutputStream has no effect, so we don't use a use block here. - data.toByteArray().inputStream().copyTo(os) - data.reset() + data.let { + // Closing a ByteArrayOutputStream has no effect, so we don't use a use block here. + it.toByteArray().inputStream().copyTo(os) + it.reset() + } } public companion object { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index ab9503b69..0fb0428a7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -25,6 +25,22 @@ import org.gradle.api.tasks.Internal public open class ComponentsXmlResourceTransformer : ResourceTransformer { private val components = mutableMapOf() + @get:Internal + internal val transformedResource: ByteArray + get() { + val os = ByteArrayOutputStream(1024 * 4) + XmlStreamWriter(os).use { writer -> + val dom = Xpp3Dom("component-set") + val componentDom = Xpp3Dom("components") + dom.addChild(componentDom) + for (component in components.values) { + componentDom.addChild(component) + } + Xpp3DomWriter.write(writer, dom) + } + return os.toByteArray() + } + override fun canTransformResource(element: FileTreeElement): Boolean { return COMPONENTS_XML_PATH == element.path } @@ -85,22 +101,6 @@ public open class ComponentsXmlResourceTransformer : ResourceTransformer { override fun hasTransformedResource(): Boolean = components.isNotEmpty() - @get:Internal - internal val transformedResource: ByteArray - get() { - val os = ByteArrayOutputStream(1024 * 4) - XmlStreamWriter(os).use { writer -> - val dom = Xpp3Dom("component-set") - val componentDom = Xpp3Dom("components") - dom.addChild(componentDom) - for (component in components.values) { - componentDom.addChild(component) - } - Xpp3DomWriter.write(writer, dom) - } - return os.toByteArray() - } - public companion object { public const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index f34a1c38e..887e485fb 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -46,6 +46,7 @@ public open class GroovyExtensionModuleTransformer : ResourceTransformer { override fun transform(context: TransformerContext) { val props = Properties() props.load(context.inputStream) + @Suppress("JavaMapForEach") props.forEach { key, value -> when (key as String) { KEY_MODULE_NAME -> handle(key, value as String) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index 8f78196c7..cc2a25bfa 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -48,9 +48,7 @@ public open class Log4j2PluginsCacheFileTransformer : ResourceTransformer { /** * @return `true` if any dat file collected. */ - override fun hasTransformedResource(): Boolean { - return tempFiles.isNotEmpty() - } + override fun hasTransformedResource(): Boolean = tempFiles.isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { try { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 4f331a0c5..aed3c054a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -212,9 +212,7 @@ public open class PropertiesFileTransformer @Inject constructor( return mergeSeparator } - override fun hasTransformedResource(): Boolean { - return propertiesEntries.isNotEmpty() - } + override fun hasTransformedResource(): Boolean = propertiesEntries.isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { // Cannot close the writer as the OutputStream needs to remain open. diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 4141b675b..f6cf39a52 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -80,7 +80,7 @@ class SimpleRelocatorTest { // Include with Regex var relocator = SimpleRelocator( "org.foo", - includes = listOf("%regex[org/foo/R(\\\$.*)?\$]"), + includes = listOf("%regex[org/foo/R(\\$.*)?$]"), ) assertThat(relocator.canRelocatePath("org/foo/R.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/R\$string.class")).isTrue() @@ -148,7 +148,7 @@ class SimpleRelocatorTest { var relocator = SimpleRelocator("org/foo", rawString = true) assertThat(relocator.canRelocatePath("(I)org/foo/bar/Class;")).isTrue() - relocator = SimpleRelocator("^META-INF/org.foo.xml\$", rawString = true) + relocator = SimpleRelocator("^META-INF/org.foo.xml$", rawString = true) assertThat(relocator.canRelocatePath("META-INF/org.foo.xml")).isTrue() } @@ -216,7 +216,7 @@ class SimpleRelocatorTest { assertThat(relocator.relocatePath("(I)Lorg/foo/bar/Class;")) .isEqualTo("(I)Lhidden/org/foo/bar/Class;") - relocator = SimpleRelocator("^META-INF/org.foo.xml\$", "META-INF/hidden.org.foo.xml", rawString = true) + relocator = SimpleRelocator("^META-INF/org.foo.xml$", "META-INF/hidden.org.foo.xml", rawString = true) assertThat(relocator.relocatePath("META-INF/org.foo.xml")) .isEqualTo("META-INF/hidden.org.foo.xml") } @@ -243,7 +243,7 @@ class SimpleRelocatorTest { excludes = listOf("org/apache/iceberg/spark/parquet/**", "org/apache/spark/sql/execution/datasources/parquet/**"), ) assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isFalse() - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet\$.class")).isFalse() + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class")).isFalse() assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isFalse() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() } @@ -255,7 +255,7 @@ class SimpleRelocatorTest { excludes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]"), ) assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isFalse() - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet\$.class")).isFalse() + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class")).isFalse() assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isFalse() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() } @@ -266,7 +266,7 @@ class SimpleRelocatorTest { includes = listOf("org/apache/iceberg/spark/parquet/**", "org/apache/spark/sql/execution/datasources/parquet/**"), ) assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isTrue() - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet\$.class")).isTrue() + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class")).isTrue() assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isFalse() } @@ -277,7 +277,7 @@ class SimpleRelocatorTest { includes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]"), ) assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isTrue() - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet\$.class")).isTrue() + assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class")).isTrue() assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isFalse() } From 5ed4e8376b49c3039e683e9d433f9b825996e591 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 23 Jul 2025 16:29:45 +0800 Subject: [PATCH 552/941] Support skipping string constant remapping (#1401) * Add `isSkipStringLiteral` for `Relocator` * Check `mapLiterals` in `RelocatorRemapper` * Make isSkipStringLiteral configurable * Test `canDisableRelocateStringConstants` * Fix `canDisableRelocateStringConstants` * Dump API * Update changelog * Update changelog * Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Cleanups * Mark `skipStringLiteral` as `Input` * Rename `skipStringLiteral` to `skipStringConstants` * Update doc * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update docs/configuration/relocation/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Revert "Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt" This reverts commit d5060b1fbf5cd0ed378122ae446c4e3eedc1a0ff. * Update changelog --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 6 +- docs/changes/README.md | 1 + docs/configuration/relocation/README.md | 57 +++++++++++++++++++ .../gradle/plugins/shadow/RelocationTest.kt | 51 +++++++++++++---- .../shadow/internal/RelocatorRemapper.kt | 19 ++++--- .../plugins/shadow/relocation/Relocator.kt | 9 +++ .../shadow/relocation/SimpleRelocator.kt | 3 + 7 files changed, 128 insertions(+), 18 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index bea4872d9..a82d5997b 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -112,6 +112,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/reloc public abstract fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; public abstract fun canRelocateClass (Ljava/lang/String;)Z public abstract fun canRelocatePath (Ljava/lang/String;)Z + public fun getSkipStringConstants ()Z public abstract fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; public abstract fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; } @@ -127,7 +128,8 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;)V public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;Z)V - public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZZ)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Ljava/util/List;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V public fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; public fun canRelocateClass (Ljava/lang/String;)Z public fun canRelocatePath (Ljava/lang/String;)Z @@ -135,10 +137,12 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public fun exclude (Ljava/lang/String;)V public final fun getExcludes ()Ljava/util/Set; public final fun getIncludes ()Ljava/util/Set; + public fun getSkipStringConstants ()Z public fun hashCode ()I public fun include (Ljava/lang/String;)V public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; + public fun setSkipStringConstants (Z)V } public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter : java/io/Serializable { diff --git a/docs/changes/README.md b/docs/changes/README.md index b9ea958ca..7bd6cf4cd 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -4,6 +4,7 @@ **Added** +- Support skipping string constant remapping. ([#1401](https://github.com/GradleUp/shadow/pull/1401)) - Let `assemble` depend on `shadowJar`. ([#1524](https://github.com/GradleUp/shadow/pull/1524)) - Fail build when inputting AAR files or using Shadow with AGP. ([#1530](https://github.com/GradleUp/shadow/pull/1530)) diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 3cfe0e8d0..c8fc680dd 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -93,6 +93,63 @@ expression in `%regex[]` before passing it to `include`/`exclude`. } ``` +## Skipping Relocation for String Constants + +If there is a class like: + +```java +package foo; + +public class Bar { + public static void main(String[] args) { + System.out.println("foo.Bar"); + } +} +``` + +in your project, and you configure the relocation like: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + relocate("foo", "my.foo") + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + relocate 'foo', 'my.foo' + } + ``` + +the string constant `"foo.Bar"` will be relocated to `"my.foo.Bar"` by default. This may not be what you want, you can +skip relocating string constants in the classes like: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + relocate("foo", "my.foo") { + // Optionally, defaults to `false`. + skipStringConstants = true + } + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + relocate('foo', 'my.foo') { + // Optionally, defaults to `false`. + skipStringConstants = true + } + } + ``` + ## Automatically Relocating Dependencies Shadow is shipped with a task that can be used to automatically configure all packages from all dependencies to be diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 083d955ce..d8c42802c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -585,16 +585,7 @@ class RelocationTest : BasePluginTest() { @Test fun relocateStringConstantsByDefault() { - writeClass { - """ - package my; - public class Main { - public static void main(String[] args) { - System.out.println("junit.framework.Test"); - } - } - """.trimIndent() - } + writeClassWithStringRef() projectScriptPath.appendText( """ $shadowJar { @@ -614,6 +605,46 @@ class RelocationTest : BasePluginTest() { ) } + @Issue( + "https://github.com/GradleUp/shadow/issues/232", + ) + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun canDisableRelocateStringConstants(skipStringConstants: Boolean) { + writeClassWithStringRef() + projectScriptPath.appendText( + """ + $shadowJar { + manifest { + attributes '$mainClassAttributeKey': 'my.Main' + } + relocate('junit', 'foo.junit') { + skipStringConstants = $skipStringConstants + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + val result = runProcess("java", "-jar", outputShadowJar.use { it.toString() }) + + val expected = if (skipStringConstants) "junit.framework.Test" else "foo.junit.framework.Test" + assertThat(result).contains(expected) + } + + private fun writeClassWithStringRef() { + writeClass { + """ + package my; + public class Main { + public static void main(String[] args) { + System.out.println("junit.framework.Test"); + } + } + """.trimIndent() + } + } + private companion object { @JvmStatic fun prefixProvider() = listOf( diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index d362996b1..232f5eccd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -19,13 +19,20 @@ internal class RelocatorRemapper( override fun mapValue(value: Any): Any { return if (value is String) { - map(value) + mapName(value, mapLiterals = true) } else { super.mapValue(value) } } - override fun map(name: String): String { + override fun map(internalName: String): String = mapName(internalName) + + fun mapPath(path: String): String { + val dotIndex = path.indexOf('.') + return if (dotIndex == -1) path else map(path.take(dotIndex)) + } + + private fun mapName(name: String, mapLiterals: Boolean = false): String { var newName = name var prefix = "" var suffix = "" @@ -38,7 +45,9 @@ internal class RelocatorRemapper( } for (relocator in relocators) { - if (relocator.canRelocateClass(newName)) { + if (mapLiterals && relocator.skipStringConstants) { + return name + } else if (relocator.canRelocateClass(newName)) { return prefix + relocator.relocateClass(newName) + suffix } else if (relocator.canRelocatePath(newName)) { return prefix + relocator.relocatePath(newName) + suffix @@ -47,8 +56,4 @@ internal class RelocatorRemapper( return name } - - fun mapPath(path: String): String { - return map(path.substringBefore('.')) - } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt index 073c29726..50616f83e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.relocation import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer +import org.gradle.api.tasks.Input /** * Modified from [org.apache.maven.plugins.shade.relocation.Relocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java). @@ -19,6 +20,14 @@ public interface Relocator { public fun applyToSourceContent(sourceContent: String): String + /** + * Indicates whether this relocator should skip relocating string constants. + * + * Defaults to `false`. + */ + @get:Input + public val skipStringConstants: Boolean get() = false + public companion object { public val ROLE: String = Relocator::class.java.name } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index ad2d7d0e5..1ae34df27 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -19,6 +19,7 @@ public open class SimpleRelocator @JvmOverloads constructor( includes: List? = null, excludes: List? = null, private val rawString: Boolean = false, + @get:Input override var skipStringConstants: Boolean = false, ) : Relocator { private val pattern: String private val pathPattern: String @@ -137,6 +138,7 @@ public open class SimpleRelocator @JvmOverloads constructor( if (this === other) return true if (other !is SimpleRelocator) return false return rawString == other.rawString && + skipStringConstants == other.skipStringConstants && pattern == other.pattern && pathPattern == other.pathPattern && shadedPattern == other.shadedPattern && @@ -149,6 +151,7 @@ public open class SimpleRelocator @JvmOverloads constructor( override fun hashCode(): Int { var result = rawString.hashCode() + result = 31 * result + skipStringConstants.hashCode() result = 31 * result + pattern.hashCode() result = 31 * result + pathPattern.hashCode() result = 31 * result + shadedPattern.hashCode() From cfbda136d27de7294008c9c83dac1196ae796d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabriel=20F=C3=A9o?= Date: Wed, 23 Jul 2025 09:37:18 +0100 Subject: [PATCH 553/941] Restore Develocity Build Scan integration (#1505) * Add IP compatibility test * Add IP-incompatible Develocity integration Makes IP compatibility test fail. * Fix IP compatibility Fix by accessing the "buildScan" extension that's available since Develocity Gradle plugin 4.0. * Fix IP compatibility if using older Develocity plugin * Use recommended artifact coordinates * Cleanups * Fix condition and Task configuration * Fix runtime usage of Develocity classes Move usage out of the plugin into a separate class/file. Since the plugin is a compileOnly dependency, it must only be referenced if the plugin/extension is present at runtime, which includes imports. ``` org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin class 'com.github.jengelman.gradle.plugins.shadow.ShadowPlugin'. > org.gradle.api.plugins.PluginInstantiationException: Could not create plugin of type 'ShadowPlugin'. > org.gradle.internal.instantiation.ClassGenerationException: Could not generate a decorated class for type ShadowPlugin. > java.lang.NoClassDefFoundError: com/gradle/develocity/agent/gradle/scan/BuildScanConfiguration > java.lang.ClassNotFoundException: com.gradle.develocity.agent.gradle.scan.BuildScanConfiguration ``` Issue caught by functional tests: https://scans.gradle.com/s/k2zkknzaf3ex4 * Fix spotless violation * Update legacy ABI * Remove 2nd extension lookup * Refactor integration * Add didWork custom value * Avoid publishing Scan during test * Cleanups * Use `findOptionalProperty` * Move and rename --------- Co-authored-by: Goooler --- api/shadow.api | 5 +++ build.gradle.kts | 2 ++ docs/changes/README.md | 4 +++ gradle/libs.versions.toml | 1 + .../gradle/plugins/shadow/BasePluginTest.kt | 4 +++ .../gradle/plugins/shadow/JavaPluginTest.kt | 31 +++++++++++++++++++ .../gradle/plugins/shadow/ShadowPlugin.kt | 18 +++++++++++ .../plugins/shadow/internal/GradleCompat.kt | 16 ++++++++++ 8 files changed, 81 insertions(+) diff --git a/api/shadow.api b/api/shadow.api index a82d5997b..8e0c59b18 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -66,11 +66,16 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowPlugin : org/gradle/api/Plugin { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowPlugin$Companion; + public static final field ENABLE_DEVELOCITY_INTEGRATION_PROPERTY Ljava/lang/String; public fun ()V public synthetic fun apply (Ljava/lang/Object;)V public fun apply (Lorg/gradle/api/Project;)V } +public final class com/github/jengelman/gradle/plugins/shadow/ShadowPlugin$Companion { +} + public abstract class com/github/jengelman/gradle/plugins/shadow/legacy/LegacyShadowPlugin : org/gradle/api/Plugin { public fun ()V public synthetic fun apply (Ljava/lang/Object;)V diff --git a/build.gradle.kts b/build.gradle.kts index b2a6fc6c3..97f5f1c35 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -107,6 +107,7 @@ develocity { } dependencies { + compileOnly(libs.develocity) compileOnly(libs.kotlin.kmp) api(libs.apache.ant) // Types from Ant are exposed in the public API. implementation(libs.apache.commonsIo) @@ -119,6 +120,7 @@ dependencies { testPluginClasspath(libs.agp) testPluginClasspath(libs.foojayResolver) + testPluginClasspath(libs.develocity) testPluginClasspath(libs.kotlin.kmp) testPluginClasspath(libs.pluginPublish) diff --git a/docs/changes/README.md b/docs/changes/README.md index 7bd6cf4cd..dd7df9cce 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -12,6 +12,10 @@ - Honor `options.release` for target JVM attribute. ([#1502](https://github.com/GradleUp/shadow/pull/1502)) +**Changed** +- Restore Develocity Build Scan integration. ([#1505](https://github.com/GradleUp/shadow/pull/1505)) + It is still disabled by default, you can enable it by setting `com.gradleup.shadow.enableDevelocityIntegration = true`. + ## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 > This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ba42ffbe9..1ef1e3b62 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,6 +19,7 @@ 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:1.0.0" +develocity = "com.gradle:develocity-gradle-plugin:4.0.2" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } # AGP version should match the min Gradle version used in tests. 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 f5908ae2e..658fa4978 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 @@ -427,6 +427,10 @@ abstract class BasePluginTest { "-Dorg.gradle.configuration-cache.parallel=true", ) + // TODO: enable this flag for all tests once we have fixed all issues with isolated projects. + // See https://github.com/GradleUp/shadow/pull/1139. + const val IP_ARGUMENT = "-Dorg.gradle.unsafe.isolated-projects=true" + fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } fun implementationFiles(vararg paths: Path): String { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index b205fa180..9aa61b2dd 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -16,6 +16,7 @@ import assertk.assertions.isNull import assertk.assertions.isTrue import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin.Companion.ENABLE_DEVELOCITY_INTEGRATION_PROPERTY import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey @@ -34,6 +35,7 @@ import kotlin.io.path.appendText import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.name import kotlin.io.path.outputStream +import kotlin.io.path.readText import kotlin.io.path.writeText import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME @@ -795,6 +797,35 @@ class JavaPluginTest : BasePluginTest() { } } + @Test + fun integrateWithDevelocityBuildScan() { + writeClientAndServerModules() + settingsScriptPath.writeText( + """ + plugins { + id 'com.gradle.develocity' + } + ${settingsScriptPath.readText()} + """.trimIndent(), + ) + + val result = run( + serverShadowJarTask, + IP_ARGUMENT, + "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", + "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a file instead. + "--info", + ) + + assertThat(result.output).all { + contains( + "Enabling Develocity integration for Shadow plugin.", + "Build scan written", + ) + doesNotContain("Configuration cache problems") + } + } + private fun dependencies(configuration: String, vararg flags: String): String { return run("dependencies", "--configuration", configuration, *flags).output } 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 1f0b52a8f..7d176d2f1 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 @@ -1,6 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.internal.KOTLIN_MULTIPLATFORM_PLUGIN_ID +import com.github.jengelman.gradle.plugins.shadow.internal.addBuildScanCustomValues +import com.github.jengelman.gradle.plugins.shadow.internal.findOptionalProperty import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import org.gradle.api.Plugin import org.gradle.api.Project @@ -28,6 +30,7 @@ public abstract class ShadowPlugin : Plugin { "See https://developer.android.com/build/publish-library/fused-library", ) } + project.configureBuildScan() // Apply the legacy plugin last. // Because we apply the ShadowJavaPlugin/ShadowApplication plugin in a withType callback for the @@ -36,4 +39,19 @@ public abstract class ShadowPlugin : Plugin { // the behavior with the old plugin when applying in that order. apply(LegacyShadowPlugin::class.java) } + + private fun Project.configureBuildScan() { + val enableDevelocityIntegration = findOptionalProperty(ENABLE_DEVELOCITY_INTEGRATION_PROPERTY)?.toBoolean() ?: false + if (enableDevelocityIntegration) { + logger.info("Enabling Develocity integration for Shadow plugin.") + } else { + logger.info("Skipping Develocity integration for Shadow plugin.") + return + } + addBuildScanCustomValues() + } + + public companion object { + public const val ENABLE_DEVELOCITY_INTEGRATION_PROPERTY: String = "com.gradleup.shadow.enableDevelocityIntegration" + } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index 262b95ab2..a5090dbf5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -1,5 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.gradle.develocity.agent.gradle.DevelocityConfiguration import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.distribution.DistributionContainer @@ -40,6 +42,20 @@ internal inline val Project.javaPluginExtension: JavaPluginExtension internal inline val Project.javaToolchainService: JavaToolchainService get() = extensions.getByType(JavaToolchainService::class.java) +@Suppress("GradleProjectIsolation") // TODO: we can't call 'providers.gradleProperty' instead due to https://github.com/gradle/gradle/issues/23572. +internal fun Project.findOptionalProperty(propertyName: String): String? = findProperty(propertyName)?.toString() + +internal fun Project.addBuildScanCustomValues() { + val develocity = extensions.findByType(DevelocityConfiguration::class.java) ?: return + val buildScan = develocity.buildScan + tasks.withType(ShadowJar::class.java).configureEach { task -> + buildScan.buildFinished { + buildScan.value("shadow.${task.path}.executed", "true") + buildScan.value("shadow.${task.path}.didWork", task.didWork.toString()) + } + } +} + internal inline val TaskContainer.jar: TaskProvider get() = named("jar", Jar::class.java) From 15b8cb534d1b2505395c5db4fd385c08a5009558 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Jul 2025 08:53:20 +0000 Subject: [PATCH 554/941] Update dependency com.gradle:develocity-gradle-plugin to v4.1 (#1537) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1ef1e3b62..c2e334738 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ 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:1.0.0" -develocity = "com.gradle:develocity-gradle-plugin:4.0.2" +develocity = "com.gradle:develocity-gradle-plugin:4.1" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } # AGP version should match the min Gradle version used in tests. From e18dde68c9c05fa9d75aeab0fb7f4eb1f3b740b6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 23 Jul 2025 17:01:17 +0800 Subject: [PATCH 555/941] Expose AbstractDependencyFilter from internal to public (#1538) --- api/shadow.api | 15 ++++ docs/changes/README.md | 2 + .../internal/AbstractDependencyFilter.kt | 81 ------------------- .../internal/DefaultDependencyFilter.kt | 3 +- .../internal/MinimizeDependencyFilter.kt | 3 +- .../plugins/shadow/tasks/DependencyFilter.kt | 74 +++++++++++++++++ 6 files changed, 95 insertions(+), 83 deletions(-) delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt diff --git a/api/shadow.api b/api/shadow.api index 8e0c59b18..62f4de3b0 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -159,6 +159,21 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; } +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter$AbstractDependencyFilter : com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter { + public fun (Lorg/gradle/api/Project;Ljava/util/List;Ljava/util/List;)V + public synthetic fun (Lorg/gradle/api/Project;Ljava/util/List;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun dependency (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; + public fun exclude (Lorg/gradle/api/specs/Spec;)V + protected final fun getExcludeSpecs ()Ljava/util/List; + protected final fun getIncludeSpecs ()Ljava/util/List; + public fun include (Lorg/gradle/api/specs/Spec;)V + protected final fun isIncluded (Lorg/gradle/api/artifacts/ResolvedDependency;)Z + public fun project (Ljava/lang/Object;)Lorg/gradle/api/specs/Spec; + public fun resolve (Ljava/util/Collection;)Lorg/gradle/api/file/FileCollection; + protected abstract fun resolve (Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;)V + public fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; +} + public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest : org/gradle/api/java/archives/Manifest { public fun inheritFrom ([Ljava/lang/Object;)V public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)V diff --git a/docs/changes/README.md b/docs/changes/README.md index dd7df9cce..49cffd170 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -15,6 +15,8 @@ **Changed** - Restore Develocity Build Scan integration. ([#1505](https://github.com/GradleUp/shadow/pull/1505)) It is still disabled by default, you can enable it by setting `com.gradleup.shadow.enableDevelocityIntegration = true`. +- Expose `AbstractDependencyFilter` from `internal` to `public`. ([#1538](https://github.com/GradleUp/shadow/pull/1538)) + You can access it via `com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter.AbstractDependencyFilter`. ## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt deleted file mode 100644 index 9743ccbf4..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/AbstractDependencyFilter.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.internal - -import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter -import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ProjectDependency -import org.gradle.api.artifacts.ResolvedArtifact -import org.gradle.api.artifacts.ResolvedDependency -import org.gradle.api.file.FileCollection -import org.gradle.api.provider.Provider -import org.gradle.api.specs.Spec - -internal sealed class AbstractDependencyFilter( - @Transient private val project: Project, - @Transient protected val includeSpecs: MutableList> = mutableListOf(), - @Transient protected val excludeSpecs: MutableList> = mutableListOf(), -) : DependencyFilter { - - protected abstract fun resolve( - dependencies: Set, - includedDependencies: MutableSet, - excludedDependencies: MutableSet, - ) - - override fun resolve(configuration: Configuration): FileCollection { - val included = mutableSetOf() - val excluded = mutableSetOf() - resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, included, excluded) - return project.files(configuration.files) - - project.files(excluded.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) - } - - override fun resolve(configurations: Collection): FileCollection { - return configurations.map { resolve(it) } - .reduceOrNull { acc, fileCollection -> acc + fileCollection } - ?: project.files() - } - - override fun exclude(spec: Spec) { - excludeSpecs.add(spec) - } - - override fun include(spec: Spec) { - includeSpecs.add(spec) - } - - override fun project(notation: Any): Spec { - @Suppress("UNCHECKED_CAST") - val realNotation = when (notation) { - is ProjectDependency -> return notation.toSpec() - is Provider<*> -> mapOf("path" to notation.get()) - is String -> mapOf("path" to notation) - is Map<*, *> -> notation as Map - else -> throw IllegalArgumentException("Unsupported notation type: ${notation::class.java}") - } - return project.dependencies.project(realNotation).toSpec() - } - - override fun dependency(dependencyNotation: Any): Spec { - val realNotation = when (dependencyNotation) { - is Provider<*> -> dependencyNotation.get() - else -> dependencyNotation - } - return project.dependencies.create(realNotation).toSpec() - } - - protected fun ResolvedDependency.isIncluded(): Boolean { - val include = includeSpecs.isEmpty() || includeSpecs.any { it.isSatisfiedBy(this) } - val exclude = excludeSpecs.isNotEmpty() && excludeSpecs.any { it.isSatisfiedBy(this) } - return include && !exclude - } - - private fun Dependency.toSpec(): Spec { - return Spec { resolvedDependency -> - (group == null || resolvedDependency.moduleGroup.matches(group!!.toRegex())) && - resolvedDependency.moduleName.matches(name.toRegex()) && - (version == null || resolvedDependency.moduleVersion.matches(version!!.toRegex())) - } - } -} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt index b10468286..309e27081 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt @@ -1,11 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter import org.gradle.api.Project import org.gradle.api.artifacts.ResolvedDependency internal class DefaultDependencyFilter( project: Project, -) : AbstractDependencyFilter(project) { +) : DependencyFilter.AbstractDependencyFilter(project) { override fun resolve( dependencies: Set, includedDependencies: MutableSet, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt index 44b4d3a55..506ef562e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt @@ -1,11 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow.internal +import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter import org.gradle.api.Project import org.gradle.api.artifacts.ResolvedDependency internal class MinimizeDependencyFilter( project: Project, -) : AbstractDependencyFilter(project) { +) : DependencyFilter.AbstractDependencyFilter(project) { override fun resolve( dependencies: Set, includedDependencies: MutableSet, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index d17f806ae..75a1a3529 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -1,9 +1,14 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import java.io.Serializable +import org.gradle.api.Project import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.artifacts.ResolvedArtifact import org.gradle.api.artifacts.ResolvedDependency import org.gradle.api.file.FileCollection +import org.gradle.api.provider.Provider import org.gradle.api.specs.Spec // DependencyFilter is used as Gradle Input in ShadowJar, so it must be Serializable. @@ -37,4 +42,73 @@ public interface DependencyFilter : Serializable { * Create a [Spec] that matches the provided [dependencyNotation]. */ public fun dependency(dependencyNotation: Any): Spec + + public abstract class AbstractDependencyFilter( + @Transient private val project: Project, + @Transient protected val includeSpecs: MutableList> = mutableListOf(), + @Transient protected val excludeSpecs: MutableList> = mutableListOf(), + ) : DependencyFilter { + + protected abstract fun resolve( + dependencies: Set, + includedDependencies: MutableSet, + excludedDependencies: MutableSet, + ) + + override fun resolve(configuration: Configuration): FileCollection { + val included = mutableSetOf() + val excluded = mutableSetOf() + resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, included, excluded) + return project.files(configuration.files) - + project.files(excluded.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) + } + + override fun resolve(configurations: Collection): FileCollection { + return configurations.map { resolve(it) } + .reduceOrNull { acc, fileCollection -> acc + fileCollection } + ?: project.files() + } + + override fun exclude(spec: Spec) { + excludeSpecs.add(spec) + } + + override fun include(spec: Spec) { + includeSpecs.add(spec) + } + + override fun project(notation: Any): Spec { + @Suppress("UNCHECKED_CAST") + val realNotation = when (notation) { + is ProjectDependency -> return notation.toSpec() + is Provider<*> -> mapOf("path" to notation.get()) + is String -> mapOf("path" to notation) + is Map<*, *> -> notation as Map + else -> throw IllegalArgumentException("Unsupported notation type: ${notation::class.java}") + } + return project.dependencies.project(realNotation).toSpec() + } + + override fun dependency(dependencyNotation: Any): Spec { + val realNotation = when (dependencyNotation) { + is Provider<*> -> dependencyNotation.get() + else -> dependencyNotation + } + return project.dependencies.create(realNotation).toSpec() + } + + protected fun ResolvedDependency.isIncluded(): Boolean { + val include = includeSpecs.isEmpty() || includeSpecs.any { it.isSatisfiedBy(this) } + val exclude = excludeSpecs.isNotEmpty() && excludeSpecs.any { it.isSatisfiedBy(this) } + return include && !exclude + } + + private fun Dependency.toSpec(): Spec { + return Spec { resolvedDependency -> + (group == null || resolvedDependency.moduleGroup.matches(group!!.toRegex())) && + resolvedDependency.moduleName.matches(name.toRegex()) && + (version == null || resolvedDependency.moduleVersion.matches(version!!.toRegex())) + } + } + } } From 5fedfd7fd77605be7e0a0ab4b95b3ad95e4b8812 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 23 Jul 2025 17:14:03 +0800 Subject: [PATCH 556/941] Update changelog --- docs/changes/README.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 49cffd170..6e1f7aab7 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -8,22 +8,25 @@ - Let `assemble` depend on `shadowJar`. ([#1524](https://github.com/GradleUp/shadow/pull/1524)) - Fail build when inputting AAR files or using Shadow with AGP. ([#1530](https://github.com/GradleUp/shadow/pull/1530)) -**Fixed** - -- Honor `options.release` for target JVM attribute. ([#1502](https://github.com/GradleUp/shadow/pull/1502)) - **Changed** + - Restore Develocity Build Scan integration. ([#1505](https://github.com/GradleUp/shadow/pull/1505)) It is still disabled by default, you can enable it by setting `com.gradleup.shadow.enableDevelocityIntegration = true`. - Expose `AbstractDependencyFilter` from `internal` to `public`. ([#1538](https://github.com/GradleUp/shadow/pull/1538)) You can access it via `com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter.AbstractDependencyFilter`. +**Fixed** + +- Honor `options.release` for target JVM attribute. ([#1502](https://github.com/GradleUp/shadow/pull/1502)) + ## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 -> This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing -significant improvements to maintainability, performance, and future extensibility. It introduces many new features, -enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult -the [new doc site](https://gradleup.com/shadow/) before upgrading. +!!! warning + + This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing + significant improvements to maintainability, performance, and future extensibility. It introduces many new features, + enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult + the [new doc site](https://gradleup.com/shadow/) before upgrading. **BREAKING** From 47a9313fd9819afa075d704ca656cc75fbaf0937 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 23 Jul 2025 17:18:27 +0800 Subject: [PATCH 557/941] Prepare version 9.0.0-rc2 --- docs/changes/README.md | 9 ++++++++- gradle.properties | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 6e1f7aab7..e8e3f5ce1 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,13 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc1...HEAD) - 2025-xx-xx +## [9.0.0-rc2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc2) - 2025-07-23 + +!!! warning + + This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing + significant improvements to maintainability, performance, and future extensibility. It introduces many new features, + enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult + the [new doc site](https://gradleup.com/shadow/) before upgrading. **Added** diff --git a/gradle.properties b/gradle.properties index f4a1af2be..8cd1d8129 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-rc2 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From d9811966efa5bf15118a5f6af4c6426601f6314c Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 23 Jul 2025 17:19:52 +0800 Subject: [PATCH 558/941] Prepare next development version --- docs/changes/README.md | 4 ++++ gradle.properties | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index e8e3f5ce1..6a612167f 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,5 +1,9 @@ # Change Log + +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc2...HEAD) - 2025-xx-xx + + ## [9.0.0-rc2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc2) - 2025-07-23 !!! warning diff --git a/gradle.properties b/gradle.properties index 8cd1d8129..f4a1af2be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-rc2 +VERSION_NAME=9.0.0-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 169071fb27334f60f933d65c802d62097414e5bd Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 25 Jul 2025 12:14:15 +0800 Subject: [PATCH 559/941] Rename enableRelocation to enableAutoRelocation and CLI option cleanups (#1541) * Comments for `enableRelocation` and `relocationPrefix` * Rename `enableRelocation` to `enableAutoRelocation` * Update docs and tests * Add dots * Update docs --- api/shadow.api | 2 +- docs/changes/README.md | 12 +++++++++++ docs/configuration/relocation/README.md | 6 +++--- docs/getting-started/README.md | 12 +++++------ docs/gradle-plugins/README.md | 4 ++-- .../gradle/plugins/shadow/JavaPluginTest.kt | 12 +++++------ .../gradle/plugins/shadow/RelocationTest.kt | 16 +++++++-------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 20 +++++++++++-------- 8 files changed, 50 insertions(+), 34 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 62f4de3b0..ec31a39a7 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -203,7 +203,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar protected abstract fun getArchiveOperations ()Lorg/gradle/api/file/ArchiveOperations; public fun getConfigurations ()Lorg/gradle/api/provider/SetProperty; public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; - public fun getEnableRelocation ()Lorg/gradle/api/provider/Property; + public fun getEnableAutoRelocation ()Lorg/gradle/api/provider/Property; public fun getExcludes ()Ljava/util/Set; public fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getIncludes ()Ljava/util/Set; diff --git a/docs/changes/README.md b/docs/changes/README.md index 6a612167f..f69a7191a 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,18 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc2...HEAD) - 2025-xx-xx +**Changed** + +- **BREAKING CHANGE:** Rename `ShadowJar`'s `enableRelocation` to `enableAutoRelocation`. ([#1541](https://github.com/GradleUp/shadow/pull/1541)) + The Command Line options are also updated: + ``` + --enable-auto-relocation Enables auto relocation of packages in the dependencies. + --no-enable-auto-relocation Disables option --enable-auto-relocation. + --minimize-jar Minimizes the jar by removing unused classes. + --no-minimize-jar Disables option --minimize-jar. + --relocation-prefix Prefix used for auto relocation of packages in the dependencies. + --rerun Causes the task to be re-run even if up-to-date. + ``` ## [9.0.0-rc2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc2) - 2025-07-23 diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index c8fc680dd..a1bb6f3de 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -156,14 +156,14 @@ Shadow is shipped with a task that can be used to automatically configure all pa relocated. This feature was formally shipped into a 2nd plugin (`com.github.johnrengelman.plugin-shadow`) but has been removed for clarity reasons in version 4.0.0. -To configure automatic dependency relocation, set `enableRelocation = true` and optionally specify a custom +To configure automatic dependency relocation, set `enableAutoRelocation = true` and optionally specify a custom `relocationPrefix` to override the default value of `"shadow"`. === "Kotlin" ```kotlin tasks.shadowJar { - enableRelocation = true + enableAutoRelocation = true relocationPrefix = "myapp" } ``` @@ -172,7 +172,7 @@ To configure automatic dependency relocation, set `enableRelocation = true` and ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation = true + enableAutoRelocation = true relocationPrefix = "myapp" } ``` diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index b8f7f8bc7..b03d72c83 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -148,12 +148,12 @@ build script. Passing property values on the command line is particularly helpfu Here are the options that can be passed to the `shadowJar`: ``` ---enable-relocation Enable relocation of packages in the jar ---no-enable-relocation Disables option --enable-relocation ---minimize-jar Minimize the jar by removing unused classes ---no-minimize-jar Disables option --minimize-jar ---relocation-prefix Prefix to use for relocated packages ---rerun Causes the task to be re-run even if up-to-date +--enable-auto-relocation Enables auto relocation of packages in the dependencies. +--no-enable-auto-relocation Disables option --enable-auto-relocation. +--minimize-jar Minimizes the jar by removing unused classes. +--no-minimize-jar Disables option --minimize-jar. +--relocation-prefix Prefix used for auto relocation of packages in the dependencies. +--rerun Causes the task to be re-run even if up-to-date. ``` Also, you can view more information about the [`ShadowJar`][ShadowJar] task by running the following command: diff --git a/docs/gradle-plugins/README.md b/docs/gradle-plugins/README.md index c12b06a33..c717cf3d9 100644 --- a/docs/gradle-plugins/README.md +++ b/docs/gradle-plugins/README.md @@ -29,7 +29,7 @@ task for relocation. } tasks.shadowJar { - enableRelocation = true + enableAutoRelocation = true archiveClassifier = "" } ``` @@ -52,7 +52,7 @@ task for relocation. } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableRelocation = true + enableAutoRelocation = true archiveClassifier = '' } ``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 9aa61b2dd..8cd0b5dff 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -87,7 +87,7 @@ class JavaPluginTest : BasePluginTest() { // Check self properties. with(shadowTask) { assertThat(minimizeJar.get()).isFalse() - assertThat(enableRelocation.get()).isFalse() + assertThat(enableAutoRelocation.get()).isFalse() assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) assertThat(configurations.get()).all { isNotEmpty() @@ -103,11 +103,11 @@ class JavaPluginTest : BasePluginTest() { val result = run("help", "--task", shadowJarTask) assertThat(result.output).contains( - "--enable-relocation Enable relocation of packages in the jar", - "--no-enable-relocation Disables option --enable-relocation", - "--minimize-jar Minimize the jar by removing unused classes", - " --no-minimize-jar Disables option --minimize-jar", - "--relocation-prefix Prefix to use for relocated packages", + "--enable-auto-relocation Enables auto relocation of packages in the dependencies.", + "--no-enable-auto-relocation Disables option --enable-auto-relocation.", + "--minimize-jar Minimizes the jar by removing unused classes.", + "--no-minimize-jar Disables option --minimize-jar.", + "--relocation-prefix Prefix used for auto relocation of packages in the dependencies.", ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index d8c42802c..8a14c08be 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -35,7 +35,7 @@ class RelocationTest : BasePluginTest() { implementation 'junit:junit:3.8.2' } $shadowJar { - enableRelocation = true + enableAutoRelocation = true relocationPrefix = '$relocationPrefix' } """.trimIndent(), @@ -357,7 +357,7 @@ class RelocationTest : BasePluginTest() { @ParameterizedTest @MethodSource("preserveLastModifiedProvider") - fun preserveLastModifiedCorrectly(enableRelocation: Boolean, preserveFileTimestamps: Boolean) { + fun preserveLastModifiedCorrectly(enableAutoRelocation: Boolean, preserveFileTimestamps: Boolean) { // Minus 3 sec to avoid the time difference between the file system and the JVM. val currentTimeMillis = System.currentTimeMillis() - 3.seconds.inWholeMilliseconds val junitEntryTimeRange = junitRawEntries.map { it.time }.let { it.min()..it.max() } @@ -368,7 +368,7 @@ class RelocationTest : BasePluginTest() { implementation 'junit:junit:3.8.2' } $shadowJar { - enableRelocation = $enableRelocation + enableAutoRelocation = $enableAutoRelocation preserveFileTimestamps = $preserveFileTimestamps } """.trimIndent(), @@ -376,7 +376,7 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) - if (enableRelocation) { + if (enableAutoRelocation) { val (relocatedEntries, otherEntries) = outputShadowJar.use { it.entries().toList().partition { entry -> entry.name.startsWith("shadow/") } } @@ -544,7 +544,7 @@ class RelocationTest : BasePluginTest() { @ParameterizedTest @MethodSource("relocationCliOptionProvider") - fun enableRelocationByCliOption(enableRelocation: Boolean, relocationPrefix: String) { + fun enableAutoRelocationByCliOption(enableAutoRelocation: Boolean, relocationPrefix: String) { val mainClassEntry = writeClass() projectScriptPath.appendText( """ @@ -557,14 +557,14 @@ class RelocationTest : BasePluginTest() { .filterNot { it.startsWith("$relocationPrefix/META-INF/") } .toTypedArray() - if (enableRelocation) { - run(shadowJarTask, "--enable-relocation", "--relocation-prefix=$relocationPrefix") + if (enableAutoRelocation) { + run(shadowJarTask, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") } else { run(shadowJarTask, "--relocation-prefix=$relocationPrefix") } assertThat(outputShadowJar).useAll { - if (enableRelocation) { + if (enableAutoRelocation) { containsOnly( "my/", "$relocationPrefix/", diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index b63133c4e..bc09cc56a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -67,12 +67,12 @@ public abstract class ShadowJar : } /** - * Minimize the jar by removing unused classes. + * Minimizes the jar by removing unused classes. * * Defaults to `false`. */ @get:Input - @get:Option(option = "minimize-jar", description = "Minimize the jar by removing unused classes") + @get:Option(option = "minimize-jar", description = "Minimizes the jar by removing unused classes.") public open val minimizeJar: Property = objectFactory.property(false) @get:Classpath @@ -135,21 +135,25 @@ public abstract class ShadowJar : } /** - * Enable relocation of packages in the jar. + * Enables auto relocation of packages in the dependencies. * * Defaults to `false`. + * + * @see relocationPrefix */ @get:Input - @get:Option(option = "enable-relocation", description = "Enable relocation of packages in the jar") - public open val enableRelocation: Property = objectFactory.property(false) + @get:Option(option = "enable-auto-relocation", description = "Enables auto relocation of packages in the dependencies.") + public open val enableAutoRelocation: Property = objectFactory.property(false) /** - * Prefix to use for relocated packages. + * Prefix used for auto relocation of packages in the dependencies. * * Defaults to `shadow`. + * + * @see enableAutoRelocation */ @get:Input - @get:Option(option = "relocation-prefix", description = "Prefix to use for relocated packages") + @get:Option(option = "relocation-prefix", description = "Prefix used for auto relocation of packages in the dependencies.") public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) @Internal @@ -380,7 +384,7 @@ public abstract class ShadowJar : private val packageRelocators: List get() { - if (!enableRelocation.get()) return emptyList() + if (!enableAutoRelocation.get()) return emptyList() val prefix = relocationPrefix.get() return includedDependencies.files.flatMap { file -> JarFile(file).use { jarFile -> From c8e686b417ce073913ffba057f6176cc9ac6b7d9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 26 Jul 2025 19:58:09 +0800 Subject: [PATCH 560/941] Update changelog sections to use h3 (#1542) --- docs/changes/README.md | 124 ++++++++++++++++++++--------------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index f69a7191a..f75d234fc 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,7 +3,7 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc2...HEAD) - 2025-xx-xx -**Changed** +### Changed - **BREAKING CHANGE:** Rename `ShadowJar`'s `enableRelocation` to `enableAutoRelocation`. ([#1541](https://github.com/GradleUp/shadow/pull/1541)) The Command Line options are also updated: @@ -25,20 +25,20 @@ enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult the [new doc site](https://gradleup.com/shadow/) before upgrading. -**Added** +### Added - Support skipping string constant remapping. ([#1401](https://github.com/GradleUp/shadow/pull/1401)) - Let `assemble` depend on `shadowJar`. ([#1524](https://github.com/GradleUp/shadow/pull/1524)) - Fail build when inputting AAR files or using Shadow with AGP. ([#1530](https://github.com/GradleUp/shadow/pull/1530)) -**Changed** +### Changed - Restore Develocity Build Scan integration. ([#1505](https://github.com/GradleUp/shadow/pull/1505)) It is still disabled by default, you can enable it by setting `com.gradleup.shadow.enableDevelocityIntegration = true`. - Expose `AbstractDependencyFilter` from `internal` to `public`. ([#1538](https://github.com/GradleUp/shadow/pull/1538)) You can access it via `com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter.AbstractDependencyFilter`. -**Fixed** +### Fixed - Honor `options.release` for target JVM attribute. ([#1502](https://github.com/GradleUp/shadow/pull/1502)) @@ -78,7 +78,7 @@ - Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip the files, only copy the files as-is. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -**Added** +### Added - Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) - Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) @@ -94,7 +94,7 @@ - Set `Main-Class` attr for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) - Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) -**Changed** +### Changed - Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) - Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) @@ -116,7 +116,7 @@ - Bump the min Gradle requirement to 8.11. ([#1479](https://github.com/GradleUp/shadow/pull/1479)) - Expose Ant as `compile` scope. ([#1488](https://github.com/GradleUp/shadow/pull/1488)) -**Fixed** +### Fixed - Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) - Adjust property initializations and modifiers in `ShadowJar`. This fixes the regression for registering custom `ShadowJar` tasks. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) @@ -136,23 +136,23 @@ ## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18 -**Fixed** +### Fixed - Fix compatibility for Gradle 9.0.0 RC1. ([#1468](https://github.com/GradleUp/shadow/pull/1468)) ## [9.0.0-beta16](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta16) - 2025-06-14 -**Changed** +### Changed - Update ASM to 9.8 to support Java 25. ([#1380](https://github.com/GradleUp/shadow/pull/1380)) -**Fixed** +### Fixed - Restore removed `Named` from `ResourceTransformer`. ([#1449](https://github.com/GradleUp/shadow/pull/1449)) ## [9.0.0-beta15](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta15) - 2025-05-28 -**Fixed** +### Fixed - Pin the plugin's Kotlin language level on 2.0. ([#1448](https://github.com/GradleUp/shadow/pull/1448)) The language level used in `9.0.0-beta14` is 2.2, which may cause compatibility issues for the plugins depending on @@ -160,29 +160,29 @@ ## [9.0.0-beta14](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta14) - 2025-05-28 -**Changed** +### Changed - Update start script templates. ([#1419](https://github.com/GradleUp/shadow/pull/1419)) - In-development snapshots are now published to the Central Portal Snapshots repository at https://central.sonatype.com/repository/maven-snapshots/. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) -**Fixed** +### Fixed - Allow using file trees of JARs together with the configuration cache. ([#1441](https://github.com/GradleUp/shadow/pull/1441)) - Fallback `RelocateClassContext` and `RelocatePathContext` to data classes. ([#1445](https://github.com/GradleUp/shadow/pull/1445)) ## [9.0.0-beta13](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta13) - 2025-04-29 -**Changed** +### Changed - Set `Main-Class` attr for KMP 1.9.0 or above. ([#1410](https://github.com/GradleUp/shadow/pull/1410)) -**Fixed** +### Fixed - Avoid creating jvm targets eagerly for KMP. ([#1378](https://github.com/GradleUp/shadow/pull/1378)) ## [9.0.0-beta12](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12) - 2025-04-01 -**Added** +### Added - Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) @@ -197,20 +197,20 @@ Options: --rerun Causes the task to be re-run even if up-to-date ``` -**Changed** +### Changed - Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) ## [9.0.0-beta11](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta11) - 2025-03-18 -**Added** +### Added - 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 for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) -**Changed** +### Changed - **BREAKING CHANGE:** Polish `ShadowSpec`. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. @@ -220,25 +220,25 @@ Options: returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) - `runShadow` no longer depends on `installShadowDist`. ([#1353](https://github.com/GradleUp/shadow/pull/1353)) -**Fixed** +### Fixed - Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) -**Removed** +### Removed - **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) ## [9.0.0-beta10](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta10) - 2025-03-05 -**Added** +### Added - Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) You still need to manually configure `manifest.attributes` (e.g. `Main-Class` attr) in the `shadowJar` task if necessary. -**Changed** +### Changed - **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) @@ -250,22 +250,22 @@ Options: `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) - Migrate doc sites to MkDocs. ([#1302](https://github.com/GradleUp/shadow/pull/1302)) -**Fixed** +### Fixed - Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) -**Removed** +### Removed - **BREAKING CHANGE:** Remove `Named` from the parents of `Transformer`. ([#1289](https://github.com/GradleUp/shadow/pull/1289)) ## [9.0.0-beta9](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta9) - 2025-02-24 -**Added** +### Added - Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) -**Changed** +### Changed - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) @@ -290,14 +290,14 @@ Options: - Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -**Fixed** +### Fixed - Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) Shadow recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. Now we honor `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. - Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -**Removed** +### Removed - **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) - **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) @@ -307,18 +307,18 @@ Options: ## [9.0.0-beta8](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8) - 2025-02-08 -**Added** +### Added - Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) - Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) -**Changed** +### Changed - **BREAKING CHANGE:** Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) - Bump min Java requirement to 11. ([#1242](https://github.com/GradleUp/shadow/pull/1242)) -**Removed** +### Removed - **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) @@ -326,13 +326,13 @@ Options: ## [9.0.0-beta7](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta7) - 2025-02-02 -**Added** +### Added - Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) - Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) -**Changed** +### Changed - Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) - Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) @@ -340,36 +340,36 @@ Options: `ShadowSpec.stats` is removed and `ShadowJar.stats` is `internal` for now. - Polish `startShadowScripts` task registering. ([#1216](https://github.com/GradleUp/shadow/pull/1216)) -**Fixed** +### Fixed - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) - Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) -**Removed** +### Removed - **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) ## [9.0.0-beta6](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta6) - 2025-01-23 -**Added** +### Added - Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) -**Fixed** +### Fixed - Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) ## [9.0.0-beta5](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta5) - 2025-01-21 -**Added** +### Added - Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) -**Changed** +### Changed - Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) - **BREAKING CHANGE:** Migrate all `ListProperty` usages to @@ -380,18 +380,18 @@ Options: - Support configuring `separator` in `AppendingTransformer`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) This is useful for handling files like `resources/application.yml`. -**Fixed** +### Fixed - Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) ## [9.0.0-beta4](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta4) - 2024-12-06 -**Changed** +### Changed - **BREAKING CHANGE:** Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as `SetProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) -**Fixed** +### Fixed - Adjust property initializations and modifiers in `ShadowJar`. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) @@ -399,18 +399,18 @@ Options: ## [9.0.0-beta2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta2) - 2024-11-28 -**Fixed** +### Fixed - Revert "Migrate SimpleRelocator to using lazy properties" ([#1052](https://github.com/GradleUp/shadow/pull/1052)) This fixes the relocation not working in `v9.0.0-beta1`. ## [9.0.0-beta1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta1) - 2024-11-27 -**Added** +### Added - Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) -**Changed** +### Changed - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) - **BREAKING CHANGE:** Migrate `Transformer`s to using lazy @@ -423,39 +423,39 @@ Options: - **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) -**Removed** +### Removed - **BREAKING CHANGE:** Remove Develocity integration. ([#1014](https://github.com/GradleUp/shadow/pull/1014)) -**Fixed** +### Fixed - Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) ## [8.3.8](https://github.com/GradleUp/shadow/releases/tag/8.3.8) - 2025-07-01 -**Fixed** +### Fixed - Fix the regression of `PropertiesFileTransformer` in `8.3.7`. ([#1493](https://github.com/GradleUp/shadow/pull/1493)) -**Changed** +### Changed - Expose Ant as `compile` scope. ([#1488](https://github.com/GradleUp/shadow/pull/1488)) ## [8.3.7](https://github.com/GradleUp/shadow/releases/tag/8.3.7) - 2025-06-24 -**Fixed** +### Fixed - Fix compatibility for Gradle 9.0.0 RC1. ([#1470](https://github.com/GradleUp/shadow/pull/1470)) ## [8.3.6](https://github.com/GradleUp/shadow/releases/tag/8.3.6) - 2025-02-02 -**Added** +### Added - Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) ## [8.3.5](https://github.com/GradleUp/shadow/releases/tag/8.3.5) - 2024-11-03 -**Fixed** +### Fixed - Revert "Bump Java level to 11" ([#1011](https://github.com/GradleUp/shadow/issues/1011)). This reverts the change to maintain compatibility with 8.x versions. The Java level will be bumped to 11 or above in @@ -463,24 +463,24 @@ Options: ## [8.3.4](https://github.com/GradleUp/shadow/releases/tag/8.3.4) - 2024-10-29 -**Fixed** +### Fixed - Apply legacy plugin last, and declare capabilities for old plugins, fixes [#964](https://github.com/GradleUp/shadow/issues/964). ([#991](https://github.com/GradleUp/shadow/pull/991)) ## [8.3.3](https://github.com/GradleUp/shadow/releases/tag/8.3.3) - 2024-10-02 -**Changed** +### Changed - Disable Develocity integration by default. ([#993](https://github.com/GradleUp/shadow/pull/993)) ## [8.3.2](https://github.com/GradleUp/shadow/releases/tag/8.3.2) - 2024-09-18 -**Added** +### Added - Support Java 23. ([#974](https://github.com/GradleUp/shadow/pull/974)) -**Changed** +### Changed - `ShadowExtension.component` has been deprecated, now you can use `component.shadow` instead. ([#956](https://github.com/GradleUp/shadow/pull/956)) @@ -488,25 +488,25 @@ Options: to [jdependency 2.11](https://github.com/tcurdt/jdependency/releases/tag/jdependency-2.11), this requires Java 11 or above to run. ([#974](https://github.com/GradleUp/shadow/pull/974)) -**Fixed** +### Fixed - Stop publishing Shadow self fat jar to Maven repository. ([#967](https://github.com/GradleUp/shadow/pull/967)) ## [8.3.1](https://github.com/GradleUp/shadow/releases/tag/8.3.1) - 2024-09-10 -**Added** +### Added - Apply an empty plugin that has the legacy `com.github.johnrengelman.shadow` plugin ID. This allows existing build logic to keep on reacting to the legacy plugin as the replacement is drop-in currently. -**Fixed** +### Fixed - Explicitly add classifier to maven publication. ([#904](https://github.com/GradleUp/shadow/pull/904)) - Refix excluding Gradle APIs for java-gradle-plugin. ([#948](https://github.com/GradleUp/shadow/pull/948)) ## [8.3.0](https://github.com/GradleUp/shadow/releases/tag/8.3.0) - 2024-08-08 -**Changed** +### Changed - **BREAKING CHANGE:** the GitHub has been transferred from `johnrengelman/shadow` to `GradleUp/shadow`, you can view more details in [GradleUp/shadow/issues/908](https://github.com/GradleUp/shadow/issues/908). @@ -516,7 +516,7 @@ Options: - Support Java 21. ([#876](https://github.com/GradleUp/shadow/pull/876)) - Use new file permission API from Gradle 8.3. ([#876](https://github.com/GradleUp/shadow/pull/876)) -**Fixed** +### Fixed - Fix for PropertiesFileTransformer breaks Reproducible builds in `8.1.1`. ([#858](https://github.com/GradleUp/shadow/pull/858)) From 1ac9bd2b54aa0410c8a956f98b3322c893476c39 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 26 Jul 2025 21:28:08 +0800 Subject: [PATCH 561/941] Changelog cleanups for 9.x versions (#1543) * Update items * Group items for rc1 * Remove outdated items * Cleanups * Update docs/changes/README.md --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 236 +++++++++++++++++++---------------------- 1 file changed, 112 insertions(+), 124 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index f75d234fc..d95153101 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -51,88 +51,108 @@ enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult the [new doc site](https://gradleup.com/shadow/) before upgrading. -**BREAKING** - -- Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) - Some APIs are marked as `internal`, and there are serial ABI changes. -- Remove Develocity integration. ([#1014](https://github.com/GradleUp/shadow/pull/1014)) -- Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) -- Migrate `ShadowJar` to using lazy properties. `isEnableRelocation` is removed, use `enableRelocation` instead. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) -- Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) -- Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as `SetProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) -- Migrate all `ListProperty` usages to `SetProperty`. Some public `List` parameters are also changed to `Set`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) -- Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) -- `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) -- Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) -- Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) -- Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) -- Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) -- Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) -- Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) -- Move `DependencyFilter` from `com.github.jengelman.gradle.plugins.shadow.internal` into `com.github.jengelman.gradle.plugins.shadow.tasks`. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) -- Rename `Transformer` to `ResourceTransformer`. Aims to better align with the name of `org.apache.maven.plugins.shade.resource.ResourceTransformer.java` and to distinguish itself from `org.gradle.api.Transformer.java`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) -- Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) -- Polish `ShadowSpec`. Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. `ShadowSpec` no longer extends `CopySpec`. Overload `relocate`, `transform` and things for better usability in Kotlin. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) -- Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) -- Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) -- Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip the files, only copy the files as-is. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -- Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - ### Added - Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) - Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) +- Support configuring `separator` in `AppendingTransformer`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) + This is useful for handling files like `resources/application.yml`. - Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) - Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) -- Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) - Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) - Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) - Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) -- Compat Kotlin Multiplatform plugin. You still need to manually configure `manifest.attributes` (e.g. `Main-Class` attr) in the `shadowJar` task if necessary. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) +- Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) - 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 for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) - Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) + ``` + --enable-relocation Enable relocation of packages in the jar + --no-enable-relocation Disables option --enable-relocation + --minimize-jar Minimize the jar by removing unused classes + --no-minimize-jar Disables option --minimize-jar + --relocation-prefix Prefix to use for relocated packages + --rerun Causes the task to be re-run even if up-to-date + ``` ### Changed -- Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) +- **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) +- **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) +- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) +- **BREAKING CHANGE:** `ShadowJar`'s `isEnableRelocation` has been renamed to `enableRelocation`. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) +- **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) +- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) +- **BREAKING CHANGE:** Some public getters have been updated in `SimpleRelocator`. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) +- **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) + Some public `List` parameters are also changed to `Set`. +- **BREAKING CHANGE:** Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) +- **BREAKING CHANGE:** Bump min Java requirement to 11. ([#1242](https://github.com/GradleUp/shadow/pull/1242)) +- **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) +- Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) +- **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) +- **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial + issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip + the files, only copy the files as-is. If you still want to shadow the unzipped files, try out something like: + ```kotlin + tasks.shadowJar { + from(zipTree(files('path/to/your/file.zip'))) + } + ``` + or + ```kotlin + dependencies { + implementation(files('path/to/your/file.zip')) + } + ``` +- **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) + Aims to better align with the name `org.apache.maven.plugins.shade.resource.ResourceTransformer.java` + and to distinguish itself from `org.gradle.api.Transformer.java`. +- **BREAKING CHANGE:** Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) +- **BREAKING CHANGE:** Polish `ShadowSpec`. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) + - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. + - `ShadowSpec` no longer extends `CopySpec`. + - Overload `relocate`, `transform` and things for better usability in Kotlin. +- **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) - Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) -- Support configuring `separator` in `AppendingTransformer`. This is useful for handling files like `resources/application.yml`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) -- Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) ([#1419](https://github.com/GradleUp/shadow/pull/1419)) -- Mark `ShadowJar.dependencyFilter` as `@Input`. `ShadowSpec.stats` is removed and `ShadowJar.stats` is `internal` for now. ([#1206](https://github.com/GradleUp/shadow/pull/1206)) +- Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) - Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) +- Mark `ShadowJar.dependencyFilter` as `@Input`. ([#1206](https://github.com/GradleUp/shadow/pull/1206)) - Polish `startShadowScripts` task registering. ([#1216](https://github.com/GradleUp/shadow/pull/1216)) -- Bump min Java requirement to 11. ([#1242](https://github.com/GradleUp/shadow/pull/1242)) -- Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) +- Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - Migrate doc sites to MkDocs. ([#1302](https://github.com/GradleUp/shadow/pull/1302)) - `runShadow` no longer depends on `installShadowDist`. ([#1353](https://github.com/GradleUp/shadow/pull/1353)) - Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) -- Set `Main-Class` attr for KMP 1.9.0 or above. ([#1410](https://github.com/GradleUp/shadow/pull/1410)) -- In-development snapshots are now published to the Central Portal Snapshots repository at https://central.sonatype.com/repository/maven-snapshots/. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) -- Update ASM to 9.8 to support Java 25. ([#1380](https://github.com/GradleUp/shadow/pull/1380)) -- Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -- Don't re-add suppressed Gradle API to `compileOnly` configuration. ([#1422](https://github.com/GradleUp/shadow/pull/1422)) -- Bump the min Gradle requirement to 8.11. ([#1479](https://github.com/GradleUp/shadow/pull/1479)) -- Expose Ant as `compile` scope. ([#1488](https://github.com/GradleUp/shadow/pull/1488)) +- In-development snapshots are now published to the Central Portal Snapshots repository. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) ### Fixed - Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) -- Adjust property initializations and modifiers in `ShadowJar`. This fixes the regression for registering custom `ShadowJar` tasks. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) - Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) - Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) - Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) +- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + Shadow recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. + Now we honor `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. +- Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) - Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) -- Avoid creating jvm targets eagerly for KMP. ([#1378](https://github.com/GradleUp/shadow/pull/1378)) - Allow using file trees of JARs together with the configuration cache. ([#1441](https://github.com/GradleUp/shadow/pull/1441)) -- Fallback `RelocateClassContext` and `RelocatePathContext` to data classes. ([#1445](https://github.com/GradleUp/shadow/pull/1445)) -- Pin the plugin's Kotlin language level on 2.0. The language level used in `9.0.0-beta14` is 2.2, which may cause compatibility issues for the plugins depending on Shadow. ([#1448](https://github.com/GradleUp/shadow/pull/1448)) -- Fix compatibility for Gradle 9.0.0 RC1. ([#1468](https://github.com/GradleUp/shadow/pull/1468)) -- Honor `DuplicatesStrategy`. Shadow recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. Now we honor `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -- Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + +### Removed + +- **BREAKING CHANGE:** Remove Develocity integration. ([#1014](https://github.com/GradleUp/shadow/pull/1014)) +- **BREAKING CHANGE:** Some public getters and setters have been removed in `SimpleRelocator`. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) +- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) +- **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) +- **BREAKING CHANGE:** Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) +- **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) +- **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) +- **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- **BREAKING CHANGE:** Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) +- **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) ## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18 @@ -163,7 +183,7 @@ ### Changed - Update start script templates. ([#1419](https://github.com/GradleUp/shadow/pull/1419)) -- In-development snapshots are now published to the Central Portal Snapshots repository at https://central.sonatype.com/repository/maven-snapshots/. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) +- In-development snapshots are now published to the Central Portal Snapshots repository. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) ### Fixed @@ -185,17 +205,14 @@ ### Added - Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) - -``` -Options: - ---enable-relocation Enable relocation of packages in the jar ---no-enable-relocation Disables option --enable-relocation ---minimize-jar Minimize the jar by removing unused classes ---no-minimize-jar Disables option --minimize-jar ---relocation-prefix Prefix to use for relocated packages ---rerun Causes the task to be re-run even if up-to-date -``` + ``` + --enable-relocation Enable relocation of packages in the jar + --no-enable-relocation Disables option --enable-relocation + --minimize-jar Minimize the jar by removing unused classes + --no-minimize-jar Disables option --minimize-jar + --relocation-prefix Prefix to use for relocated packages + --rerun Causes the task to be re-run even if up-to-date + ``` ### Changed @@ -206,48 +223,39 @@ Options: ### Added - 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)) +- Support using type-safe dependency accessors in `ShadowJar.dependencies`. ([#1322](https://github.com/GradleUp/shadow/pull/1322)) - Set `Main-Class` attr for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) ### Changed - **BREAKING CHANGE:** Polish `ShadowSpec`. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) - - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. - - `ShadowSpec` no longer extends `CopySpec`. - - Overload `relocate`, `transform` and things for better usability in Kotlin. -- **BREAKING CHANGE:** Remove redundant types from function - returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) + - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. + - `ShadowSpec` no longer extends `CopySpec`. + - Overload `relocate`, `transform` and things for better usability in Kotlin. +- **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) - `runShadow` no longer depends on `installShadowDist`. ([#1353](https://github.com/GradleUp/shadow/pull/1353)) ### Fixed -- Fix relocation exclusion for file patterns like - `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) +- Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) ### Removed -- **BREAKING CHANGE:** Reduce dependency and project overloads in - `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) +- **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) ## [9.0.0-beta10](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta10) - 2025-03-05 ### Added - Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) - You still need to manually configure `manifest.attributes` (e.g. `Main-Class` attr) 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. ### Changed -- **BREAKING CHANGE:** Rename `Transformer` to - `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) - Aims to better align with the name - of [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java) - and to distinguish itself - from [org.gradle.api.Transformer.java](https://docs.gradle.org/current/javadoc/org/gradle/api/Transformer.html). -- **BREAKING CHANGE:** Mark `DefaultInheritManifest` as - `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) +- **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) + Aims to better align with the name `org.apache.maven.plugins.shade.resource.ResourceTransformer.java` + and to distinguish itself from `org.gradle.api.Transformer.java`. +- **BREAKING CHANGE:** Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) - Migrate doc sites to MkDocs. ([#1302](https://github.com/GradleUp/shadow/pull/1302)) ### Fixed @@ -256,8 +264,7 @@ Options: ### Removed -- **BREAKING CHANGE:** Remove `Named` from the parents of - `Transformer`. ([#1289](https://github.com/GradleUp/shadow/pull/1289)) +- **BREAKING CHANGE:** Remove `Named` from the parents of `Transformer`. ([#1289](https://github.com/GradleUp/shadow/pull/1289)) ## [9.0.0-beta9](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta9) - 2025-02-24 @@ -267,11 +274,9 @@ Options: ### Changed -- **BREAKING CHANGE:** Move tracking unused classes logic out of - `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) +- **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) - Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) -- **BREAKING CHANGE:** Move `DependencyFilter` from `com.github.jengelman.gradle.plugins.shadow.internal` into - `com.github.jengelman.gradle.plugins.shadow.tasks`. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) +- **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) - **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip @@ -287,8 +292,7 @@ Options: implementation(files('path/to/your/file.zip')) } ``` -- Refactor file visiting logic in `StreamAction`, handle file unzipping via - `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) ### Fixed @@ -301,8 +305,7 @@ Options: - **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) - **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) -- **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and - `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - **BREAKING CHANGE:** Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) ## [9.0.0-beta8](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8) - 2025-02-08 @@ -310,8 +313,7 @@ Options: ### Added - Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) -- Inject `Multi-Release` manifest attribute if any dependency contains - it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) +- Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) ### Changed @@ -320,16 +322,14 @@ Options: ### Removed -- **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been - removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) +- **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) - **BREAKING CHANGE:** Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) ## [9.0.0-beta7](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta7) - 2025-02-02 ### Added -- Inject `TargetJvmVersion` attribute for Gradle Module - Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) +- Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) - Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) ### Changed @@ -343,25 +343,21 @@ Options: ### Fixed - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) -- Fix `ShadowJar` not being successful after `includes` or `excludes` are - changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) +- Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) ### Removed -- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` - task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) +- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) ## [9.0.0-beta6](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta6) - 2025-01-23 ### Added -- Exclude `module-info.class` in Multi-Release folders by - default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) +- Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) ### Fixed -- Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` - files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) +- Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) ## [9.0.0-beta5](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta5) - 2025-01-21 @@ -372,29 +368,25 @@ Options: ### Changed - Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) -- **BREAKING CHANGE:** Migrate all `ListProperty` usages to - `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) +- **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) Some public `List` parameters are also changed to `Set`. -- Replace deprecated `SelfResolvingDependency` with - `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) +- Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) - Support configuring `separator` in `AppendingTransformer`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) This is useful for handling files like `resources/application.yml`. ### Fixed -- Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) +- Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) ## [9.0.0-beta4](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta4) - 2024-12-06 ### Changed -- **BREAKING CHANGE:** Some public getters are removed from `SimpleRelocator`, `includes` and `excludes` are exposed as - `SetProperty`s. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) +- **BREAKING CHANGE:** Some public getters are removed and updated in `SimpleRelocator`. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) ### Fixed -- Adjust property initializations and modifiers in - `ShadowJar`. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) +- Adjust property initializations and modifiers in `ShadowJar`. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) This fixes the regression for registering custom `ShadowJar` tasks. ## [9.0.0-beta2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta2) - 2024-11-28 @@ -413,15 +405,11 @@ Options: ### Changed - **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) -- **BREAKING CHANGE:** Migrate `Transformer`s to using lazy - properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) -- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy - properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) +- **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) +- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) `isEnableRelocation` is removed, use `enableRelocation` instead. -- **BREAKING CHANGE:** Resolve `Configuration` directly in - `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) -- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy - properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) +- **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) +- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) ### Removed From 710289473c797cb3498225a977430658ec237fb9 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 28 Jul 2025 11:25:21 +0800 Subject: [PATCH 562/941] Add description for testPluginClasspath --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 97f5f1c35..cef1872e6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -64,6 +64,7 @@ spotless { val testPluginClasspath by configurations.registering { isCanBeResolved = true + description = "Plugins used in integration tests could be resolved in classpath." } configurations.configureEach { @@ -221,7 +222,6 @@ tasks.withType().configureEach { } tasks.pluginUnderTestMetadata { - // Plugins used in tests could be resolved in classpath. pluginClasspath.from( testPluginClasspath, ) From 77f61734d647246beda5d942232e56ae5e0f33b5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 16:13:44 +0000 Subject: [PATCH 563/941] Update dependency gradle to v9.0.0-rc-4 (#1544) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 79ce75b0c..e8a90ba69 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-rc-3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-rc-4-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 83d030cc821d8fa77c011060e194c93792541c63 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 30 Jul 2025 15:44:37 +0800 Subject: [PATCH 564/941] Document adding extra dependencies into POM files (#1546) --- docs/publishing/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 74c37ab7c..a8dff9aad 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -103,6 +103,16 @@ the `archiveClassifier` of the shadowed JAR like the following: publications { create("shadow") { from(components["shadow"]) + + // Optionally, you can add extra dependencies to the POM file like the following: + pom.withXml { + val dependenciesNode = asNode().get("dependencies") ?: asNode().appendNode("dependencies") + val node = (dependenciesNode as groovy.util.Node).appendNode("dependency") + node.appendNode("groupId", "com.squareup.retrofit2") + node.appendNode("artifactId", "converter-gson") + node.appendNode("version", "2.12.0") + node.appendNode("scope", "runtime") + } } } repositories { @@ -142,6 +152,16 @@ the `archiveClassifier` of the shadowed JAR like the following: publications { shadow(MavenPublication) { from components.shadow + + // Optionally, you can add extra dependencies to the POM file like the following: + pom.withXml { xml -> + def dependenciesNode = xml.asNode().get('dependencies') ?: xml.asNode().appendNode('dependencies') + def node = dependenciesNode.appendNode('dependency') + node.appendNode('groupId', 'com.squareup.retrofit2') + node.appendNode('artifactId', 'converter-gson') + node.appendNode('version', '2.12.0') + node.appendNode('scope', 'runtime') + } } } repositories { From 6885001462611b62f364b8b0442dc23cab898c81 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 30 Jul 2025 16:59:24 +0800 Subject: [PATCH 565/941] Note the duplicatesStrategy change as breaking (#1547) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 14 ++++++++------ .../gradle/plugins/shadow/JavaPluginTest.kt | 2 ++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index d95153101..de6e57592 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -91,6 +91,9 @@ - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) - Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) - **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) +- **BREAKING CHANGE:** Change the default `duplicatesStrategy` from `EXCLUDE` to `INCLUDE`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + - `ShadowJar` recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. + - Now `ShadowJar` honors `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. - **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip @@ -133,9 +136,7 @@ - Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) - Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) - Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) -- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - Shadow recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. - Now we honor `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. +- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) - Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) @@ -277,6 +278,9 @@ - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) - Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) - **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) +- **BREAKING CHANGE:** Change the default `duplicatesStrategy` from `EXCLUDE` to `INCLUDE`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + - `ShadowJar` recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. + - Now `ShadowJar` honors `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. - **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip @@ -296,9 +300,7 @@ ### Fixed -- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - Shadow recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. - Now we honor `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. +- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) ### Removed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 8cd0b5dff..d9b2b8a71 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -37,6 +37,7 @@ import kotlin.io.path.name import kotlin.io.path.outputStream import kotlin.io.path.readText import kotlin.io.path.writeText +import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME @@ -70,6 +71,7 @@ class JavaPluginTest : BasePluginTest() { // Check extended properties. with(shadowTask as Jar) { + assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.INCLUDE) assertThat(archiveAppendix.orNull).isNull() assertThat(archiveBaseName.get()).isEqualTo(projectName) assertThat(archiveClassifier.get()).isEqualTo("all") From 088326f7b7f94b294a0147ed151faf70c5ccdb33 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 30 Jul 2025 18:27:06 +0800 Subject: [PATCH 566/941] Test transformers for including and excluding resources (#1549) * Add func tests for `IncludeResourceTransformer` and `DontIncludeResourceTransformer` * Cleanups * Note replacing `IncludeResourceTransformer` with `ShadowJar.from` * Note replacing `DontIncludeResourceTransformer` with `ShadowJar.exclude` * Clean up `shadowJarIsCachedCorrectlyWhenUsingOtherTransformers` --- .../shadow/caching/TransformerCachingTest.kt | 5 -- .../shadow/transformers/TransformersTest.kt | 54 +++++++++++++++++-- .../DontIncludeResourceTransformer.kt | 2 + .../IncludeResourceTransformer.kt | 2 + 4 files changed, 53 insertions(+), 10 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index d53114530..73c318c9d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -181,9 +181,6 @@ class TransformerCachingTest : BaseCachingTest() { @MethodSource("transformerConfigProvider") fun shadowJarIsCachedCorrectlyWhenUsingOtherTransformers(pair: Pair>) { val (configuration, transformer) = pair - if (configuration.contains("test/some.file")) { - path("test/some.file").writeText("some content") - } val assertions = { assertCompositeExecutions { containsAtLeast(mainClass) @@ -209,9 +206,7 @@ class TransformerCachingTest : BaseCachingTest() { "" to ApacheLicenseResourceTransformer::class, "" to ApacheNoticeResourceTransformer::class, "" to ComponentsXmlResourceTransformer::class, - "" to DontIncludeResourceTransformer::class, "" to GroovyExtensionModuleTransformer::class, - "{ resource.set(\"test.file\"); file.fileValue(file(\"test/some.file\")) }" to IncludeResourceTransformer::class, "" to Log4j2PluginsCacheFileTransformer::class, "" to ManifestAppenderTransformer::class, "" to ManifestResourceTransformer::class, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 6940adad5..0d81d0f92 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -11,9 +11,11 @@ import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsOnly +import com.github.jengelman.gradle.plugins.shadow.util.getContent import com.github.jengelman.gradle.plugins.shadow.util.getStream import java.util.jar.Attributes as JarAttribute import kotlin.io.path.appendText +import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.writeText import kotlin.reflect.KClass import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE @@ -104,6 +106,53 @@ class TransformersTest : BaseTransformerTest() { } } + @Test + fun canIncludeResource() { + val foo = path("foo").apply { writeText("foo") } + projectScriptPath.appendText( + transform( + transformerBlock = """ + resource = 'bar' + file = file('${foo.invariantSeparatorsPathString}') + """.trimIndent(), + ), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsOnly( + "bar", + *manifestEntries, + ) + getContent("bar").isEqualTo("foo") + } + } + + @Test + fun canExcludeResource() { + val one = buildJarOne { + insert("foo", "bar") + insert("bar", "foo") + } + projectScriptPath.appendText( + transform( + dependenciesBlock = implementationFiles(one), + transformerBlock = "resource = 'foo'", + ), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsOnly( + "bar", + *manifestEntries, + ) + getContent("bar").isEqualTo("foo") + } + } + @Test fun canUseCustomTransformer() { projectScriptPath.appendText( @@ -133,9 +182,6 @@ class TransformersTest : BaseTransformerTest() { @MethodSource("transformerConfigProvider") fun otherTransformers(pair: Pair>) { val (configuration, transformer) = pair - if (configuration.contains("test/some.file")) { - path("test/some.file").writeText("some content") - } projectScriptPath.appendText( """ dependencies { @@ -191,8 +237,6 @@ class TransformersTest : BaseTransformerTest() { "" to ApacheLicenseResourceTransformer::class, "" to ApacheNoticeResourceTransformer::class, "" to ComponentsXmlResourceTransformer::class, - "" to DontIncludeResourceTransformer::class, - "{ resource.set(\"test.file\"); file.fileValue(file(\"test/some.file\")) }" to IncludeResourceTransformer::class, "" to ManifestAppenderTransformer::class, "" to ManifestResourceTransformer::class, ) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index 1b6f28cce..83b29fc2c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -11,6 +11,8 @@ import org.gradle.api.tasks.Optional /** * A resource processor that prevents the inclusion of an arbitrary resource into the shaded JAR. * + * You can also use [com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.exclude] instead. + * * Modified from [org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/DontIncludeResourceTransformer.java). * * @author John Engelman diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index 49e4a0b37..8b3910802 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -15,6 +15,8 @@ import org.gradle.api.tasks.PathSensitivity /** * A resource processor that allows the addition of an arbitrary file content into the shaded JAR. * + * You can also use [com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.from] instead. + * * Modified from [org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/IncludeResourceTransformer.java). * * @author John Engelman From 15e394a159a6f1e26ee67ac8b2a3d85483ea84b2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 30 Jul 2025 21:08:53 +0800 Subject: [PATCH 567/941] Add PreserveFirstFoundResourceTransformer (#1548) --- api/shadow.api | 12 +++++++ docs/changes/README.md | 6 ++++ docs/configuration/merging/README.md | 4 ++- .../shadow/transformers/TransformersTest.kt | 33 +++++++++++++++++++ .../PreserveFirstFoundResourceTransformer.kt | 32 ++++++++++++++++++ 5 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt diff --git a/api/shadow.api b/api/shadow.api index ec31a39a7..2dce33b7a 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -394,6 +394,18 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestRes public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } +public class com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { + public fun (Lorg/gradle/api/model/ObjectFactory;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + protected final fun getFound ()Ljava/util/Set; + public fun getName ()Ljava/lang/String; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public fun getResources ()Lorg/gradle/api/provider/SetProperty; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z diff --git a/docs/changes/README.md b/docs/changes/README.md index de6e57592..a489f29fc 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,12 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc2...HEAD) - 2025-xx-xx +### Added + +- Add `PreserveFirstFoundResourceTransformer`. ([#1548](https://github.com/GradleUp/shadow/pull/1548)) + This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and + want to ensure that only the first found resource is included in the final JAR. + ### Changed - **BREAKING CHANGE:** Rename `ShadowJar`'s `enableRelocation` to `enableAutoRelocation`. ([#1541](https://github.com/GradleUp/shadow/pull/1541)) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 74f37dfc9..11105853d 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -429,7 +429,8 @@ exclude the duplicated service files beforehand. However, this behavior might be Want `ResourceTransformer`s and `duplicatesStrategy` to work together? There is a way to achieve this, leave the `duplicatesStrategy` as `INCLUDE` and declare a custom [`ResourceTransformer`][ResourceTransformer] to handle the duplicated files. - +If you just want to keep the current behavior and preserve the first found resource, there is a simple built-in one to +handle this called [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer]. [AbstractCopyTask]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html @@ -440,6 +441,7 @@ duplicated files. [Log4j2PluginsCacheFileTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-log4j2-plugins-cache-file-transformer/index.html [ResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html [ServiceFileTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html +[PreserveFirstFoundResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-preserve-first-found-resource-transformer/index.html [ShadowJar.append]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html [ShadowJar.from]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action) [ShadowJar.transform]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 0d81d0f92..3e00deb79 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -153,6 +153,39 @@ class TransformersTest : BaseTransformerTest() { } } + @Test + fun canPreserveFirstFoundResource() { + path("src/main/resources/foo/bar").writeText("bar1") + path("src/main/resources/foo/baz").writeText("baz1") + val one = buildJarOne { + insert("foo/bar", "bar2") + insert("foo/baz", "baz2") + } + val two = buildJarTwo { + insert("foo/bar", "bar3") + insert("foo/baz", "baz3") + } + projectScriptPath.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = "resources = ['foo/bar']", + ), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsOnly( + "foo/", + "foo/bar", + "foo/baz", + *manifestEntries, + ) + getContent("foo/bar").isEqualTo("bar1") + getContent("foo/baz").isEqualTo("baz3") + } + } + @Test fun canUseCustomTransformer() { projectScriptPath.appendText( diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt new file mode 100644 index 000000000..f9e10cff6 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt @@ -0,0 +1,32 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.internal.setProperty +import javax.inject.Inject +import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal + +/** + * A resource processor that preserves the first resource matched and excludes all others. + * + * This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and + * want to ensure that only the first found resource is included in the final JAR. If there are multiple resources with + * the same path in a project and its dependencies, the first one found should be the projects'. + */ +@CacheableTransformer +public open class PreserveFirstFoundResourceTransformer @Inject constructor( + final override val objectFactory: ObjectFactory, +) : ResourceTransformer by ResourceTransformer.Companion { + @get:Internal + protected val found: MutableSet = mutableSetOf() + + @get:Input + public open val resources: SetProperty = objectFactory.setProperty() + + override fun canTransformResource(element: FileTreeElement): Boolean { + val path = element.path + return resources.get().contains(path) && !found.add(path) + } +} From 39d5bb1565a5aa4f987242662f86511b290211ef Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 31 Jul 2025 12:09:05 +0800 Subject: [PATCH 568/941] Fail build if the ZIP entries in the shadowed JAR are duplicate (#1552) * Add `ZipOutputStream.entries` * Add `checkDuplicateEntries` * Update changelog * Disable this flag by default * Cleanups * Add CLI option * Add tests * Improve the message of dup entries * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * `duplicated` -> `duplicate` * `Fail` -> `Fails` --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 3 +- docs/changes/README.md | 24 ++++++--- docs/configuration/merging/README.md | 6 +-- docs/getting-started/README.md | 14 ++--- .../gradle/plugins/shadow/JavaPluginTest.kt | 54 +++++++++++++++++++ .../gradle/plugins/shadow/RelocationTest.kt | 6 +-- .../plugins/shadow/tasks/ShadowCopyAction.kt | 30 +++++++++-- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 27 +++++++--- 8 files changed, 133 insertions(+), 31 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 2dce33b7a..379e15eb4 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -185,7 +185,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/tasks/InheritManif public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; - public fun (Ljava/io/File;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZLjava/lang/String;)V + public fun (Ljava/io/File;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZZLjava/lang/String;)V public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; } @@ -205,6 +205,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; public fun getEnableAutoRelocation ()Lorg/gradle/api/provider/Property; public fun getExcludes ()Ljava/util/Set; + public fun getFailOnDuplicateEntries ()Lorg/gradle/api/provider/Property; public fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getIncludes ()Ljava/util/Set; public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; diff --git a/docs/changes/README.md b/docs/changes/README.md index a489f29fc..fe7a56ebc 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -8,18 +8,26 @@ - Add `PreserveFirstFoundResourceTransformer`. ([#1548](https://github.com/GradleUp/shadow/pull/1548)) This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and want to ensure that only the first found resource is included in the final JAR. +- Fail build if the ZIP entries in the shadowed JAR are duplicate. ([#1552](https://github.com/GradleUp/shadow/pull/1552)) + This feature is controlled by the `shadowJar.failOnDuplicateEntries` property, which is `false` by default. + Related to setting `duplicatesStrategy = DuplicatesStrategy.FAIL` but there are some differences: + - It only checks the entries in the shadowed jar, not the input files. + - It works with setting `duplicatesStrategy` to any value. + - It provides a more strict check before the JAR is created. ### Changed - **BREAKING CHANGE:** Rename `ShadowJar`'s `enableRelocation` to `enableAutoRelocation`. ([#1541](https://github.com/GradleUp/shadow/pull/1541)) The Command Line options are also updated: ``` - --enable-auto-relocation Enables auto relocation of packages in the dependencies. - --no-enable-auto-relocation Disables option --enable-auto-relocation. - --minimize-jar Minimizes the jar by removing unused classes. - --no-minimize-jar Disables option --minimize-jar. - --relocation-prefix Prefix used for auto relocation of packages in the dependencies. - --rerun Causes the task to be re-run even if up-to-date. + --enable-auto-relocation Enables auto relocation of packages in the dependencies. + --no-enable-auto-relocation Disables option --enable-auto-relocation. + --fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate. + --no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries. + --minimize-jar Minimizes the jar by removing unused classes. + --no-minimize-jar Disables option --minimize-jar. + --relocation-prefix Prefix used for auto relocation of packages in the dependencies. + --rerun Causes the task to be re-run even if up-to-date. ``` ## [9.0.0-rc2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc2) - 2025-07-23 @@ -95,7 +103,7 @@ - **BREAKING CHANGE:** Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) - **BREAKING CHANGE:** Bump min Java requirement to 11. ([#1242](https://github.com/GradleUp/shadow/pull/1242)) - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) -- Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) +- Reduce duplicate `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) - **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) - **BREAKING CHANGE:** Change the default `duplicatesStrategy` from `EXCLUDE` to `INCLUDE`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - `ShadowJar` recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. @@ -282,7 +290,7 @@ ### Changed - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) -- Reduce duplicated `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) +- Reduce duplicate `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) - **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) - **BREAKING CHANGE:** Change the default `duplicatesStrategy` from `EXCLUDE` to `INCLUDE`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - `ShadowJar` recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 11105853d..c79c20cfe 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -396,7 +396,7 @@ override it like: Different strategies will lead to different results for `foo/bar` files in the JARs to be merged: - `EXCLUDE`: The **first** `foo/bar` file will be included in the final JAR. -- `FAIL`: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicated `foo/bar` files. +- `FAIL`: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. - `INCLUDE`: The **last** `foo/bar` file will be included in the final JAR (the default behavior). - `INHERIT`: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. @@ -424,11 +424,11 @@ Different strategies will lead to different results for `foo/bar` files in the J ``` The [`ServiceFileTransformer`][ServiceFileTransformer] will not work as expected because the `duplicatesStrategy` will -exclude the duplicated service files beforehand. However, this behavior might be what you expected for duplicated +exclude the duplicate service files beforehand. However, this behavior might be what you expected for duplicate `foo/bar` files, preventing them from being included. Want `ResourceTransformer`s and `duplicatesStrategy` to work together? There is a way to achieve this, leave the `duplicatesStrategy` as `INCLUDE` and declare a custom [`ResourceTransformer`][ResourceTransformer] to handle the -duplicated files. +duplicate files. If you just want to keep the current behavior and preserve the first found resource, there is a simple built-in one to handle this called [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer]. diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index b03d72c83..9048a4bf1 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -148,12 +148,14 @@ build script. Passing property values on the command line is particularly helpfu Here are the options that can be passed to the `shadowJar`: ``` ---enable-auto-relocation Enables auto relocation of packages in the dependencies. ---no-enable-auto-relocation Disables option --enable-auto-relocation. ---minimize-jar Minimizes the jar by removing unused classes. ---no-minimize-jar Disables option --minimize-jar. ---relocation-prefix Prefix used for auto relocation of packages in the dependencies. ---rerun Causes the task to be re-run even if up-to-date. +--enable-auto-relocation Enables auto relocation of packages in the dependencies. +--no-enable-auto-relocation Disables option --enable-auto-relocation. +--fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate. +--no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries. +--minimize-jar Minimizes the jar by removing unused classes. +--no-minimize-jar Disables option --minimize-jar. +--relocation-prefix Prefix used for auto relocation of packages in the dependencies. +--rerun Causes the task to be re-run even if up-to-date. ``` Also, you can view more information about the [`ShadowJar`][ShadowJar] task by running the following command: diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index d9b2b8a71..195b3cd2d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -95,6 +95,7 @@ class JavaPluginTest : BasePluginTest() { isNotEmpty() containsOnly(project.runtimeConfiguration) } + assertThat(failOnDuplicateEntries.get()).isFalse() } assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile) @@ -107,6 +108,8 @@ class JavaPluginTest : BasePluginTest() { assertThat(result.output).contains( "--enable-auto-relocation Enables auto relocation of packages in the dependencies.", "--no-enable-auto-relocation Disables option --enable-auto-relocation.", + "--fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate.", + "--no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries", "--minimize-jar Minimizes the jar by removing unused classes.", "--no-minimize-jar Disables option --minimize-jar.", "--relocation-prefix Prefix used for auto relocation of packages in the dependencies.", @@ -828,6 +831,57 @@ class JavaPluginTest : BasePluginTest() { } } + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun failBuildIfDuplicateEntries(enable: Boolean) { + path("src/main/resources/a.properties").writeText("invalid a") + projectScriptPath.appendText( + """ + dependencies { + ${implementationFiles(artifactAJar)} + } + $shadowJar { + failOnDuplicateEntries = $enable + } + """.trimIndent(), + ) + + val result = if (enable) { + runWithFailure(shadowJarTask) + } else { + run(shadowJarTask, "--info") + } + + assertThat(result.output).contains( + "Duplicate entries found in the shadowed JAR:", + "a.properties (2 times)", + ) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun failBuildIfDuplicateEntriesByCliOption(enable: Boolean) { + path("src/main/resources/a.properties").writeText("invalid a") + projectScriptPath.appendText( + """ + dependencies { + ${implementationFiles(artifactAJar)} + } + """.trimIndent(), + ) + + val result = if (enable) { + runWithFailure(shadowJarTask, "--fail-on-duplicate-entries") + } else { + run(shadowJarTask, "--info") + } + + assertThat(result.output).contains( + "Duplicate entries found in the shadowed JAR:", + "a.properties (2 times)", + ) + } + private fun dependencies(configuration: String, vararg flags: String): String { return run("dependencies", "--configuration", configuration, *flags).output } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 8a14c08be..2fa92e2f6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -544,7 +544,7 @@ class RelocationTest : BasePluginTest() { @ParameterizedTest @MethodSource("relocationCliOptionProvider") - fun enableAutoRelocationByCliOption(enableAutoRelocation: Boolean, relocationPrefix: String) { + fun enableAutoRelocationByCliOption(enable: Boolean, relocationPrefix: String) { val mainClassEntry = writeClass() projectScriptPath.appendText( """ @@ -557,14 +557,14 @@ class RelocationTest : BasePluginTest() { .filterNot { it.startsWith("$relocationPrefix/META-INF/") } .toTypedArray() - if (enableAutoRelocation) { + if (enable) { run(shadowJarTask, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") } else { run(shadowJarTask, "--relocation-prefix=$relocationPrefix") } assertThat(outputShadowJar).useAll { - if (enableAutoRelocation) { + if (enable) { containsOnly( "my/", "$relocationPrefix/", diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index c498e9e87..90b022bd2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -38,6 +38,7 @@ public open class ShadowCopyAction( private val relocators: Set, private val unusedClasses: Set, private val preserveFileTimestamps: Boolean, + private val failOnDuplicateEntries: Boolean, private val encoding: String?, ) : CopyAction { private val visitedDirs = mutableMapOf() @@ -53,8 +54,8 @@ public open class ShadowCopyAction( zipOutStream.use { zos -> stream.process(StreamAction(zos)) processTransformers(zos) - // This must be called as the last step to ensure that directories are added after all files. - addDirs(zos) + addDirs(zos) // This must be called after adding all file entries to avoid duplicate directories being added. + checkDuplicateEntries(zos) } } catch (e: Exception) { if (e is Zip64RequiredException || e.cause is Zip64RequiredException) { @@ -89,8 +90,7 @@ public open class ShadowCopyAction( private fun addDirs(zos: ZipOutputStream) { @Suppress("UNCHECKED_CAST") - val entries = zos::class.java.getDeclaredField("entries").apply { isAccessible = true } - .get(zos).cast>().map { it.name } + val entries = zos.entries.map { it.name } val added = entries.toMutableSet() val currentTimeMillis = System.currentTimeMillis() @@ -118,6 +118,22 @@ public open class ShadowCopyAction( } } + private fun checkDuplicateEntries(zos: ZipOutputStream) { + val entries = zos.entries.map { it.name } + val duplicates = entries.groupingBy { it }.eachCount().filter { it.value > 1 } + if (duplicates.isNotEmpty()) { + val dupEntries = duplicates.entries.joinToString(separator = "\n") { + "${it.key} (${it.value} times)" + } + val message = "Duplicate entries found in the shadowed JAR: \n$dupEntries" + if (failOnDuplicateEntries) { + throw GradleException(message) + } else { + logger.info(message) + } + } + } + private inner class StreamAction( private val zipOutStr: ZipOutputStream, ) : CopyActionProcessingStreamAction { @@ -237,5 +253,11 @@ public open class ShadowCopyAction( * 1980-02-01 00:00:00 (318182400000). */ public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis + + private val ZipOutputStream.entries: List + get() { + return this::class.java.getDeclaredField("entries").apply { isAccessible = true } + .get(this).cast>() + } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index bc09cc56a..2d94a175d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -156,6 +156,20 @@ public abstract class ShadowJar : @get:Option(option = "relocation-prefix", description = "Prefix used for auto relocation of packages in the dependencies.") public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) + /** + * Fails build if the ZIP entries in the shadowed JAR are duplicate. + * + * This is related to setting [duplicatesStrategy] to [DuplicatesStrategy.FAIL] but there are some differences: + * - It only checks the entries in the shadowed jar, not the input files. + * - It works with setting [duplicatesStrategy] to any value. + * - It provides a more strict check before the JAR is created. + * + * Defaults to `false`. + */ + @get:Input + @get:Option(option = "fail-on-duplicate-entries", description = "Fails build if the ZIP entries in the shadowed JAR are duplicate.") + public open val failOnDuplicateEntries: Property = objectFactory.property(false) + @Internal override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest @@ -362,12 +376,13 @@ public abstract class ShadowJar : emptySet() } return ShadowCopyAction( - archiveFile.get().asFile, - zosProvider, - transformers.get(), - relocators.get() + packageRelocators, - unusedClasses, - isPreserveFileTimestamps, + zipFile = archiveFile.get().asFile, + zosProvider = zosProvider, + transformers = transformers.get(), + relocators = relocators.get() + packageRelocators, + unusedClasses = unusedClasses, + preserveFileTimestamps = isPreserveFileTimestamps, + failOnDuplicateEntries = failOnDuplicateEntries.get(), metadataCharset, ) } From ed6c1bad73be67a97e160e113047a220b782dc2f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 31 Jul 2025 15:42:54 +0800 Subject: [PATCH 569/941] Add more docs and tests for DuplicatesStrategy (#1553) * Update `shadowJarIsCachedCorrectlyAfterDuplicatesStrategyChanged` * Override `getDuplicatesStrategy` explicitly * Remove `@Input` mark for `getDuplicatesStrategy` * Update KDOCs * Update docs * Add tests for using `filesMatching` and `eachFile` * Fix formats Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Add `@see` * Fix typos --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 1 + docs/configuration/merging/README.md | 20 ++++++--- .../shadow/caching/ShadowJarCachingTest.kt | 13 +++--- .../ServiceFileTransformerTest.kt | 44 ++++++++++++++++++- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 34 +++++++++++++- .../PreserveFirstFoundResourceTransformer.kt | 4 ++ 6 files changed, 100 insertions(+), 16 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 379e15eb4..c354fc0a7 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -203,6 +203,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar protected abstract fun getArchiveOperations ()Lorg/gradle/api/file/ArchiveOperations; public fun getConfigurations ()Lorg/gradle/api/provider/SetProperty; public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; + public fun getDuplicatesStrategy ()Lorg/gradle/api/file/DuplicatesStrategy; public fun getEnableAutoRelocation ()Lorg/gradle/api/provider/Property; public fun getExcludes ()Ljava/util/Set; public fun getFailOnDuplicateEntries ()Lorg/gradle/api/provider/Property; diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index c79c20cfe..3c6405237 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -423,17 +423,23 @@ Different strategies will lead to different results for `foo/bar` files in the J } ``` -The [`ServiceFileTransformer`][ServiceFileTransformer] will not work as expected because the `duplicatesStrategy` will -exclude the duplicate service files beforehand. However, this behavior might be what you expected for duplicate +The [`ResourceTransformer`][ResourceTransformer]s like [`ServiceFileTransformer`][ServiceFileTransformer] will not work +as expected because the `duplicatesStrategy` will exclude the duplicate service files beforehand. However, this behavior might be what you expected for duplicate `foo/bar` files, preventing them from being included. -Want `ResourceTransformer`s and `duplicatesStrategy` to work together? There is a way to achieve this, leave the -`duplicatesStrategy` as `INCLUDE` and declare a custom [`ResourceTransformer`][ResourceTransformer] to handle the -duplicate files. -If you just want to keep the current behavior and preserve the first found resource, there is a simple built-in one to -handle this called [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer]. + +Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several ways to +do it: + +- Use [`eachFile`][Jar.eachFile] or [`filesMatching`][Jar.filesMatching] to override the strategy for specific files. +- Keep `duplicatesStrategy = INCLUDE` and write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates. + +If you just want to keep the current behavior and preserve the first found resources, there is a simple built-in one +called [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer]. [AbstractCopyTask]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html +[Jar.eachFile]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:eachFile(groovy.lang.Closure) +[Jar.filesMatching]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:filesMatching(java.lang.Iterable,%20org.gradle.api.Action) [AppendingTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html [DuplicatesStrategy]: https://docs.gradle.org/current/javadoc/org/gradle/api/file/DuplicatesStrategy.html [GroovyExtensionModuleTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-groovy-extension-module-transformer/index.html diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 85049a751..9979080bc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -117,12 +117,13 @@ class ShadowJarCachingTest : BaseCachingTest() { DuplicatesStrategy.INCLUDE, DuplicatesStrategy.WARN, ).forEach { strategy -> - projectScriptPath.appendText( - """ - $shadowJar { - duplicatesStrategy = DuplicatesStrategy.$strategy - } - """.trimIndent() + System.lineSeparator(), + projectScriptPath.writeText( + getDefaultProjectBuildScript(withGroup = true, withVersion = true) + + """ + $shadowJar { + duplicatesStrategy = DuplicatesStrategy.$strategy + } + """.trimIndent(), ) assertCompositeExecutions() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 5acfdf58b..2e20e5099 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -224,6 +224,48 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } + @Test + fun strategyExcludeCanBeOverriddenByEachFile() { + writeDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) + projectScriptPath.appendText( + """ + $shadowJar { + eachFile { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) + getContent(ENTRY_SERVICES_FOO).isEqualTo("one\ntwo") + } + } + + @Test + fun strategyExcludeCanBeOverriddenByFileMatching() { + writeDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) + projectScriptPath.appendText( + """ + $shadowJar { + filesMatching('$ENTRY_SERVICES_SHADE') { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) + getContent(ENTRY_SERVICES_FOO).isEqualTo("one") + } + } + private fun writeDuplicatesStrategy(strategy: DuplicatesStrategy) { projectScriptPath.appendText( """ @@ -234,7 +276,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { duplicatesStrategy = DuplicatesStrategy.$strategy mergeServiceFiles() } - """.trimIndent(), + """.trimIndent() + System.lineSeparator(), ) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 2d94a175d..29e34db9b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -159,9 +159,9 @@ public abstract class ShadowJar : /** * Fails build if the ZIP entries in the shadowed JAR are duplicate. * - * This is related to setting [duplicatesStrategy] to [DuplicatesStrategy.FAIL] but there are some differences: + * This is related to setting [getDuplicatesStrategy] to [DuplicatesStrategy.FAIL] but there are some differences: * - It only checks the entries in the shadowed jar, not the input files. - * - It works with setting [duplicatesStrategy] to any value. + * - It works with setting [getDuplicatesStrategy] to any value. * - It provides a more strict check before the JAR is created. * * Defaults to `false`. @@ -179,6 +179,36 @@ public abstract class ShadowJar : @Input // Trigger task executions after excludes changed. override fun getExcludes(): MutableSet = super.getExcludes() + /** + * Returns the strategy to use when trying to copy more than one file to the same destination. + * + * This strategy can be overridden for individual files by using [eachFile] or [filesMatching]. + * + * The default value is [DuplicatesStrategy.INCLUDE]. Different strategies will lead to different results for + * `foo/bar` files in the JARs to be merged: + * - [DuplicatesStrategy.EXCLUDE]: The **first** `foo/bar` file will be included in the final JAR. + * - [DuplicatesStrategy.FAIL]: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. + * - [DuplicatesStrategy.INCLUDE]: The **last** `foo/bar` file will be included in the final JAR (the default behavior). + * - [DuplicatesStrategy.INHERIT]: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. + * - [DuplicatesStrategy.WARN]: The **last** `foo/bar` file will be included in the final JAR, and a warning message will be logged. + * + * **NOTE:** The strategy takes precedence over transforming and relocating. + * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to + * [DuplicatesStrategy.EXCLUDE], as the service files are excluded beforehand. Want [ResourceTransformer]s and the + * strategy to work together? There are several ways to do it: + * - Use [eachFile] or [filesMatching] to override the strategy for specific files. + * - Keep `duplicatesStrategy = INCLUDE` and write your own [ResourceTransformer] to handle duplicates. + * + * If you just want to keep the current behavior and preserve the first found resources, there is a simple built-in one + * called [com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoundResourceTransformer]. + * + * @see [eachFile] + * @see [filesMatching] + * @see [DuplicatesStrategy] + * @see [org.gradle.api.file.CopySpec.getDuplicatesStrategy] + */ + override fun getDuplicatesStrategy(): DuplicatesStrategy = super.getDuplicatesStrategy() + @get:Inject protected abstract val archiveOperations: ArchiveOperations diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt index f9e10cff6..416bf2961 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.setProperty import javax.inject.Inject +import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.SetProperty @@ -14,6 +15,9 @@ import org.gradle.api.tasks.Internal * This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and * want to ensure that only the first found resource is included in the final JAR. If there are multiple resources with * the same path in a project and its dependencies, the first one found should be the projects'. + * + * @see [DuplicatesStrategy] + * @see [com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.getDuplicatesStrategy] */ @CacheableTransformer public open class PreserveFirstFoundResourceTransformer @Inject constructor( From 5effcc81859eb405b5810467ddd5115b42749ef0 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 31 Jul 2025 16:11:42 +0800 Subject: [PATCH 570/941] Refine imports (#1554) * Optimize imports by IDEA * Shorten imports for Kdocs --- gradle/lint-baseline.xml | 12 +++---- .../shadow/caching/TransformerCachingTest.kt | 2 -- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 1 - .../plugins/shadow/tasks/ShadowCopyAction.kt | 2 -- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 33 ++++++++++++------- .../DontIncludeResourceTransformer.kt | 3 +- .../IncludeResourceTransformer.kt | 3 +- .../PreserveFirstFoundResourceTransformer.kt | 3 +- 8 files changed, 33 insertions(+), 26 deletions(-) diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index 9ed89eb90..47b7314db 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -1,5 +1,5 @@ - + @@ -151,7 +151,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -162,7 +162,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -173,7 +173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,7 +184,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 73c318c9d..dadea90e3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -6,9 +6,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.ApacheLicenseReso import com.github.jengelman.gradle.plugins.shadow.transformers.ApacheNoticeResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ComponentsXmlResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.DontIncludeResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.IncludeResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestAppenderTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestResourceTransformer 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 6413e5f41..06aa00660 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 @@ -3,7 +3,6 @@ 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.isAtLeastKgpVersion import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey -import kotlin.collections.contains import org.gradle.api.Plugin import org.gradle.api.Project import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 90b022bd2..015e510a7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -28,8 +28,6 @@ import org.objectweb.asm.commons.ClassRemapper /** * Modified from [org.gradle.api.internal.file.archive.ZipCopyAction.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java). - * - * @see [org.gradle.api.internal.file.archive.ZipCopyAction] */ public open class ShadowCopyAction( private val zipFile: File, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 29e34db9b..6dde0c84c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -16,6 +16,7 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoundResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer @@ -30,7 +31,13 @@ import org.gradle.api.Action import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ArchiveOperations import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.CopySpec import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.file.DuplicatesStrategy.EXCLUDE +import org.gradle.api.file.DuplicatesStrategy.FAIL +import org.gradle.api.file.DuplicatesStrategy.INCLUDE +import org.gradle.api.file.DuplicatesStrategy.INHERIT +import org.gradle.api.file.DuplicatesStrategy.WARN import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.provider.Property @@ -57,7 +64,7 @@ public abstract class ShadowJar : init { // https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L55-L64 - duplicatesStrategy = DuplicatesStrategy.INCLUDE + duplicatesStrategy = INCLUDE manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { @@ -159,7 +166,7 @@ public abstract class ShadowJar : /** * Fails build if the ZIP entries in the shadowed JAR are duplicate. * - * This is related to setting [getDuplicatesStrategy] to [DuplicatesStrategy.FAIL] but there are some differences: + * This is related to setting [getDuplicatesStrategy] to [FAIL] but there are some differences: * - It only checks the entries in the shadowed jar, not the input files. * - It works with setting [getDuplicatesStrategy] to any value. * - It provides a more strict check before the JAR is created. @@ -184,28 +191,30 @@ public abstract class ShadowJar : * * This strategy can be overridden for individual files by using [eachFile] or [filesMatching]. * - * The default value is [DuplicatesStrategy.INCLUDE]. Different strategies will lead to different results for + * The default value is [INCLUDE]. Different strategies will lead to different results for * `foo/bar` files in the JARs to be merged: - * - [DuplicatesStrategy.EXCLUDE]: The **first** `foo/bar` file will be included in the final JAR. - * - [DuplicatesStrategy.FAIL]: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. - * - [DuplicatesStrategy.INCLUDE]: The **last** `foo/bar` file will be included in the final JAR (the default behavior). - * - [DuplicatesStrategy.INHERIT]: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. - * - [DuplicatesStrategy.WARN]: The **last** `foo/bar` file will be included in the final JAR, and a warning message will be logged. + * + * - [EXCLUDE]: The **first** `foo/bar` file will be included in the final JAR. + * - [FAIL]: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. + * - [INCLUDE]: The **last** `foo/bar` file will be included in the final JAR (the default behavior). + * - [INHERIT]: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. + * - [WARN]: The **last** `foo/bar` file will be included in the final JAR, and a warning message will be logged. * * **NOTE:** The strategy takes precedence over transforming and relocating. * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to - * [DuplicatesStrategy.EXCLUDE], as the service files are excluded beforehand. Want [ResourceTransformer]s and the - * strategy to work together? There are several ways to do it: + * [EXCLUDE], as the service files are excluded beforehand. Want [ResourceTransformer]s and the strategy to work + * together? There are several ways to do it: + * * - Use [eachFile] or [filesMatching] to override the strategy for specific files. * - Keep `duplicatesStrategy = INCLUDE` and write your own [ResourceTransformer] to handle duplicates. * * If you just want to keep the current behavior and preserve the first found resources, there is a simple built-in one - * called [com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoundResourceTransformer]. + * called [PreserveFirstFoundResourceTransformer]. * * @see [eachFile] * @see [filesMatching] * @see [DuplicatesStrategy] - * @see [org.gradle.api.file.CopySpec.getDuplicatesStrategy] + * @see [CopySpec.duplicatesStrategy] */ override fun getDuplicatesStrategy(): DuplicatesStrategy = super.getDuplicatesStrategy() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index 83b29fc2c..4496d1528 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.property +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import javax.inject.Inject import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory @@ -11,7 +12,7 @@ import org.gradle.api.tasks.Optional /** * A resource processor that prevents the inclusion of an arbitrary resource into the shaded JAR. * - * You can also use [com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.exclude] instead. + * You can also use [ShadowJar.exclude] instead. * * Modified from [org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/DontIncludeResourceTransformer.java). * diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index 8b3910802..2f3128ff4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import javax.inject.Inject import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.RegularFileProperty @@ -15,7 +16,7 @@ import org.gradle.api.tasks.PathSensitivity /** * A resource processor that allows the addition of an arbitrary file content into the shaded JAR. * - * You can also use [com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.from] instead. + * You can also use [ShadowJar.from] instead. * * Modified from [org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/IncludeResourceTransformer.java). * diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt index 416bf2961..f031d3492 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.setProperty +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import javax.inject.Inject import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.file.FileTreeElement @@ -17,7 +18,7 @@ import org.gradle.api.tasks.Internal * the same path in a project and its dependencies, the first one found should be the projects'. * * @see [DuplicatesStrategy] - * @see [com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.getDuplicatesStrategy] + * @see [ShadowJar.getDuplicatesStrategy] */ @CacheableTransformer public open class PreserveFirstFoundResourceTransformer @Inject constructor( From 92042687ce18b58f3882564f4c45116600d03abe Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 31 Jul 2025 21:39:43 +0800 Subject: [PATCH 571/941] Remove JVM default compat stuff (#1556) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` OLD: shadow-9.0.0-SNAPSHOT-before.jar NEW: shadow-9.0.0-SNAPSHOT-after.jar JAR │ old │ new │ diff ───────┼───────────┼───────────┼────────── class │ 478 KiB │ 470.6 KiB │ -7.4 KiB other │ 14.6 KiB │ 14.6 KiB │ 0 B ───────┼───────────┼───────────┼────────── total │ 492.6 KiB │ 485.2 KiB │ -7.4 KiB CLASSES │ old │ new │ diff ─────────┼──────┼──────┼────────────── classes │ 79 │ 76 │ -3 (+0 -3) methods │ 1250 │ 1232 │ -18 (+0 -18) fields │ 211 │ 211 │ 0 (+0 -0) ================= ==== JAR ==== ================= size │ diff │ path ─────────┼──────────┼────────────────────────────────────────────────────────────────────────────────────────────────── │ -3 KiB │ - com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec$DefaultImpls.class │ -1.2 KiB │ - com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer$DefaultImpls.class │ -1 KiB │ - com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest$DefaultImpls.class 1.4 KiB │ -334 B │ ∆ com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.class 4.9 KiB │ -1.2 KiB │ ∆ com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.class 3 KiB │ -534 B │ ∆ com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.class ─────────┼──────────┼────────────────────────────────────────────────────────────────────────────────────────────────── 9.2 KiB │ -7.4 KiB │ (total) ===================== ==== CLASSES ==== ===================== CLASSES: old │ new │ diff ─────┼─────┼──────────── 79 │ 76 │ -3 (+0 -3) - com.github.jengelman.gradle.plugins.shadow.tasks.InheritManifest$DefaultImpls - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec$DefaultImpls - com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer$DefaultImpls METHODS: old │ new │ diff ──────┼──────┼────────────── 1250 │ 1232 │ -18 (+0 -18) - com.github.jengelman.gradle.plugins.shadow.tasks.InheritManifest access$inheritFrom$jd(InheritManifest, Object[]) - com.github.jengelman.gradle.plugins.shadow.tasks.InheritManifest$DefaultImpls inheritFrom(InheritManifest, Object[]) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec access$append$jd(ShadowSpec, String) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec access$relocate$jd(ShadowSpec, Relocator) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec access$relocate$jd(ShadowSpec, Class) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec access$relocate$jd(ShadowSpec, String, String) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec access$transform$jd(ShadowSpec, ResourceTransformer) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec access$transform$jd(ShadowSpec, Class) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec$DefaultImpls append(ShadowSpec, String) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec$DefaultImpls relocate(ShadowSpec, Relocator) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec$DefaultImpls relocate(ShadowSpec, Class) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec$DefaultImpls relocate(ShadowSpec, String, String) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec$DefaultImpls transform(ShadowSpec, ResourceTransformer) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec$DefaultImpls transform(ShadowSpec, Class) - com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer access$getName$jd(ResourceTransformer) → String - com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer access$getObjectFactory$jd(ResourceTransformer) → ObjectFactory - com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer$DefaultImpls getName(ResourceTransformer) → String - com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer$DefaultImpls getObjectFactory(ResourceTransformer) → ObjectFactory ``` --- api/shadow.api | 18 ------------------ docs/changes/README.md | 4 ++++ .../plugins/shadow/tasks/InheritManifest.kt | 1 - .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 1 - .../shadow/transformers/ResourceTransformer.kt | 1 - 5 files changed, 4 insertions(+), 21 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index c354fc0a7..dcb7fa435 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -179,10 +179,6 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)V } -public final class com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest$DefaultImpls { - public static fun inheritFrom (Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest;[Ljava/lang/Object;)V -} - public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; public fun (Ljava/io/File;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZZLjava/lang/String;)V @@ -252,15 +248,6 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public abstract fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)V } -public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec$DefaultImpls { - public static fun append (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Ljava/lang/String;)V - public static fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)V - public static fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Ljava/lang/Class;)V - public static fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Ljava/lang/String;Ljava/lang/String;)V - public static fun transform (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)V - public static fun transform (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec;Ljava/lang/Class;)V -} - public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z @@ -458,11 +445,6 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Resou public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public final class com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer$DefaultImpls { - public static fun getName (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Ljava/lang/String; - public static fun getObjectFactory (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)Lorg/gradle/api/model/ObjectFactory; -} - public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer, org/gradle/api/tasks/util/PatternFilterable { public fun ()V public fun (Lorg/gradle/api/tasks/util/PatternSet;)V diff --git a/docs/changes/README.md b/docs/changes/README.md index fe7a56ebc..ed1534f51 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -30,6 +30,10 @@ --rerun Causes the task to be re-run even if up-to-date. ``` +### Removed + +- Remove JVM default compat stuff. ([#1556](https://github.com/GradleUp/shadow/pull/1556)) + ## [9.0.0-rc2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc2) - 2025-07-23 !!! warning diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt index 93bf5984f..858d218fa 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt @@ -3,7 +3,6 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import org.gradle.api.Action import org.gradle.api.java.archives.Manifest -@JvmDefaultWithCompatibility public interface InheritManifest : Manifest { public fun inheritFrom(vararg inheritPaths: Any) { inheritFrom(inheritPaths = inheritPaths, action = null) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt index f0a01509c..8a49cddd6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -7,7 +7,6 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransform import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import org.gradle.api.Action -@JvmDefaultWithCompatibility public interface ShadowSpec { public fun minimize() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt index 8edcef93c..bb3d523ac 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt @@ -15,7 +15,6 @@ import org.gradle.api.tasks.Internal * @author Charlie Knudsen * @author John Engelman */ -@JvmDefaultWithCompatibility public interface ResourceTransformer : Named { public fun canTransformResource(element: FileTreeElement): Boolean From 4f8260c8dfe5c7e9dcdcf6bf47875b645265866b Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 31 Jul 2025 21:40:15 +0800 Subject: [PATCH 572/941] Fix typos for stuff --- docs/getting-started/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 9048a4bf1..5635f73bd 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -32,7 +32,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: classpath("com.gradleup.shadow:shadow-gradle-plugin:") } } - // `apply plugin` stuffs are used with `buildscript`. + // `apply plugin` stuff are used with `buildscript`. apply(plugin = "java") apply(plugin = "com.gradleup.shadow") ``` @@ -49,7 +49,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: classpath 'com.gradleup.shadow:shadow-gradle-plugin:' } } - // `apply plugin` stuffs are used with `buildscript`. + // `apply plugin` stuff are used with `buildscript`. apply plugin: 'java' apply plugin: 'com.gradleup.shadow' ``` @@ -72,7 +72,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: classpath("com.gradleup.shadow:shadow-gradle-plugin:") } } - // `apply plugin` stuffs are used with `buildscript`. + // `apply plugin` stuff are used with `buildscript`. apply(plugin = "java") apply(plugin = "com.gradleup.shadow") ``` @@ -98,7 +98,7 @@ Alternatively, the plugin can be added to the buildscript classpath and applied: classpath 'com.gradleup.shadow:shadow-gradle-plugin:' } } - // `apply plugin` stuffs are used with `buildscript`. + // `apply plugin` stuff are used with `buildscript`. apply plugin: 'java' apply plugin: 'com.gradleup.shadow' ``` From b4f5d305ad4d02385a0e1652ae3f46df8f08e4f1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 31 Jul 2025 22:23:21 +0800 Subject: [PATCH 573/941] Mark Action parameters as non-null (#1555) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ``` OLD: shadow-9.0.0-SNAPSHOT-before.jar NEW: shadow-9.0.0-SNAPSHOT-after.jar JAR │ old │ new │ diff ───────┼───────────┼───────────┼────────── class │ 470.6 KiB │ 476.3 KiB │ +5.7 KiB other │ 14.6 KiB │ 14.6 KiB │ 0 B ───────┼───────────┼───────────┼────────── total │ 485.2 KiB │ 490.9 KiB │ +5.7 KiB CLASSES │ old │ new │ diff ─────────┼──────┼──────┼────────────── classes │ 76 │ 78 │ +2 (+2 -0) methods │ 1232 │ 1248 │ +16 (+19 -3) fields │ 211 │ 213 │ +2 (+2 -0) ================= ==== JAR ==== ================= size │ diff │ path ──────────┼──────────┼──────────────────────────────────────────────────────────────────────────────────── 1.8 KiB │ +1.8 KiB │ + com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar$transform$1.class 1.7 KiB │ +1.7 KiB │ + com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar$relocate$1.class 8.6 KiB │ -38 B │ ∆ com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.class 2 KiB │ +613 B │ ∆ com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.class 42.4 KiB │ +747 B │ ∆ com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.class 5.8 KiB │ +960 B │ ∆ com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.class ──────────┼──────────┼──────────────────────────────────────────────────────────────────────────────────── 62.2 KiB │ +5.7 KiB │ (total) ===================== ==== CLASSES ==== ===================== CLASSES: old │ new │ diff ─────┼─────┼──────────── 76 │ 78 │ +2 (+2 -0) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$relocate$1 + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$transform$1 METHODS: old │ new │ diff ──────┼──────┼────────────── 1232 │ 1248 │ +16 (+19 -3) + com.github.jengelman.gradle.plugins.shadow.tasks.InheritManifest inheritFrom$lambda$0(ManifestMergeSpec) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar append$lambda$21(String, String, AppendingTransformer) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar createCopyAction$lambda$24(ShadowJar, File) → ZipOutputStream + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar mergeGroovyExtensionModules$lambda$20(GroovyExtensionModuleTransformer) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar mergeServiceFiles$lambda$18(ServiceFileTransformer) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar mergeServiceFiles$lambda$19(String, ServiceFileTransformer) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$relocate$1 () + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$relocate$1 () + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$relocate$1 execute(Relocator) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$relocate$1 execute(Object) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$transform$1 () + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$transform$1 () + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$transform$1 execute(ResourceTransformer) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$transform$1 execute(Object) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec relocate$lambda$0(SimpleRelocator) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec relocate$lambda$1(Relocator) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec relocate$lambda$2(Relocator) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec transform$lambda$3(ResourceTransformer) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowSpec transform$lambda$4(ResourceTransformer) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar append$lambda$19(String, String, AppendingTransformer) - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar createCopyAction$lambda$22(ShadowJar, File) → ZipOutputStream - com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar mergeServiceFiles$lambda$18(String, ServiceFileTransformer) FIELDS: old │ new │ diff ─────┼─────┼──────────── 211 │ 213 │ +2 (+2 -0) + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$relocate$1 INSTANCE: ShadowJar$relocate$1 + com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar$transform$1 INSTANCE: ShadowJar$transform$1 ``` --- docs/changes/README.md | 1 + .../shadow/internal/DefaultInheritManifest.kt | 6 +-- .../plugins/shadow/tasks/InheritManifest.kt | 5 ++- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 40 +++++++++---------- .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 26 ++++++------ 5 files changed, 40 insertions(+), 38 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ed1534f51..540325ddd 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -29,6 +29,7 @@ --relocation-prefix Prefix used for auto relocation of packages in the dependencies. --rerun Causes the task to be re-run even if up-to-date. ``` +- Mark `Action` parameters as non-null. ([#1555](https://github.com/GradleUp/shadow/pull/1555)) ### Removed diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt index 5de7d0381..54bad071f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt @@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.InheritManifest import org.gradle.api.Action import org.gradle.api.internal.file.FileResolver import org.gradle.api.java.archives.Manifest +import org.gradle.api.java.archives.ManifestMergeSpec import org.gradle.api.java.archives.internal.DefaultManifest import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec @@ -16,13 +17,12 @@ internal class DefaultInheritManifest @JvmOverloads constructor( override fun inheritFrom( vararg inheritPaths: Any, - action: Action<*>?, + action: Action, ) { val mergeSpec = DefaultManifestMergeSpec() mergeSpec.from(*inheritPaths) inheritMergeSpecs.add(mergeSpec) - @Suppress("UNCHECKED_CAST") - (action as? Action)?.execute(mergeSpec) + action.execute(mergeSpec) } override fun getEffectiveManifest(): DefaultManifest { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt index 858d218fa..8fe11619d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt @@ -2,11 +2,12 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import org.gradle.api.Action import org.gradle.api.java.archives.Manifest +import org.gradle.api.java.archives.ManifestMergeSpec public interface InheritManifest : Manifest { public fun inheritFrom(vararg inheritPaths: Any) { - inheritFrom(inheritPaths = inheritPaths, action = null) + inheritFrom(inheritPaths = inheritPaths, action = {}) } - public fun inheritFrom(vararg inheritPaths: Any, action: Action<*>?) + public fun inheritFrom(vararg inheritPaths: Any, action: Action) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 6dde0c84c..e18fc339e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -231,23 +231,23 @@ public abstract class ShadowJar : /** * Enable [minimizeJar] and execute the [action] with the [DependencyFilter] for minimize. */ - override fun minimize(action: Action?) { + override fun minimize(action: Action) { minimize() - action?.execute(dependencyFilterForMinimize) + action.execute(dependencyFilterForMinimize) } /** * Extra dependency operations to be applied in the shadow steps. */ - override fun dependencies(action: Action?) { - action?.execute(dependencyFilter.get()) + override fun dependencies(action: Action) { + action.execute(dependencyFilter.get()) } /** * Merge Java services files. */ override fun mergeServiceFiles() { - transform(ServiceFileTransformer::class.java, null) + transform(ServiceFileTransformer::class.java, action = {}) } /** @@ -262,7 +262,7 @@ public abstract class ShadowJar : /** * Merge Java services files with [action]. */ - override fun mergeServiceFiles(action: Action?) { + override fun mergeServiceFiles(action: Action) { transform(ServiceFileTransformer::class.java, action) } @@ -270,7 +270,7 @@ public abstract class ShadowJar : * Merge Groovy extension modules (`META-INF/**/org.codehaus.groovy.runtime.ExtensionModule`). */ override fun mergeGroovyExtensionModules() { - transform(GroovyExtensionModuleTransformer::class.java, null) + transform(GroovyExtensionModuleTransformer::class.java, action = {}) } /** @@ -295,7 +295,7 @@ public abstract class ShadowJar : override fun relocate( pattern: String, destination: String, - action: Action?, + action: Action, ) { val relocator = SimpleRelocator(pattern, destination) addRelocator(relocator, action) @@ -304,7 +304,7 @@ public abstract class ShadowJar : /** * Relocate classes and resources using a [Relocator]. */ - override fun relocate(clazz: Class, action: Action?) { + override fun relocate(clazz: Class, action: Action) { val relocator = clazz.getDeclaredConstructor().newInstance() addRelocator(relocator, action) } @@ -312,7 +312,7 @@ public abstract class ShadowJar : /** * Relocate classes and resources using a [Relocator]. */ - override fun relocate(relocator: R, action: Action?) { + override fun relocate(relocator: R, action: Action) { addRelocator(relocator, action) } @@ -322,7 +322,7 @@ public abstract class ShadowJar : * This is a convenience method for [relocate] with a reified type parameter for Kotlin. */ public inline fun relocate() { - relocate(R::class.java, null) + relocate(R::class.java, action = {}) } /** @@ -330,21 +330,21 @@ public abstract class ShadowJar : * * This is a convenience method for [relocate] with a reified type parameter for Kotlin. */ - public inline fun relocate(action: Action?) { + public inline fun relocate(action: Action) { relocate(R::class.java, action) } /** * Transform resources using a [ResourceTransformer]. */ - override fun transform(clazz: Class, action: Action?) { + override fun transform(clazz: Class, action: Action) { addTransform(clazz.create(objectFactory), action) } /** * Transform resources using a [ResourceTransformer]. */ - override fun transform(transformer: T, action: Action?) { + override fun transform(transformer: T, action: Action) { addTransform(transformer, action) } @@ -354,7 +354,7 @@ public abstract class ShadowJar : * This is a convenience method for [transform] with a reified type parameter for Kotlin. */ public inline fun transform() { - transform(T::class.java, null) + transform(T::class.java, action = {}) } /** @@ -362,7 +362,7 @@ public abstract class ShadowJar : * * This is a convenience method for [transform] with a reified type parameter for Kotlin. */ - public inline fun transform(action: Action?) { + public inline fun transform(action: Action) { transform(T::class.java, action) } @@ -426,13 +426,13 @@ public abstract class ShadowJar : ) } - private fun addRelocator(relocator: R, action: Action?) { - action?.execute(relocator) + private fun addRelocator(relocator: R, action: Action) { + action.execute(relocator) relocators.add(relocator) } - private fun addTransform(transformer: T, action: Action?) { - action?.execute(transformer) + private fun addTransform(transformer: T, action: Action) { + action.execute(transformer) transformers.add(transformer) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt index 8a49cddd6..9c487a5e7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt @@ -10,15 +10,15 @@ import org.gradle.api.Action public interface ShadowSpec { public fun minimize() - public fun minimize(action: Action?) + public fun minimize(action: Action) - public fun dependencies(action: Action?) + public fun dependencies(action: Action) public fun mergeServiceFiles() public fun mergeServiceFiles(rootPath: String) - public fun mergeServiceFiles(action: Action?) + public fun mergeServiceFiles(action: Action) public fun mergeGroovyExtensionModules() @@ -29,32 +29,32 @@ public interface ShadowSpec { public fun append(resourcePath: String, separator: String) public fun relocate(pattern: String, destination: String) { - relocate(pattern, destination, null) + relocate(pattern, destination, action = {}) } - public fun relocate(pattern: String, destination: String, action: Action?) + public fun relocate(pattern: String, destination: String, action: Action) public fun relocate(clazz: Class) { - relocate(clazz, null) + relocate(clazz, action = {}) } - public fun relocate(clazz: Class, action: Action?) + public fun relocate(clazz: Class, action: Action) public fun relocate(relocator: R) { - relocate(relocator, null) + relocate(relocator, action = {}) } - public fun relocate(relocator: R, action: Action?) + public fun relocate(relocator: R, action: Action) public fun transform(clazz: Class) { - transform(clazz, null) + transform(clazz, action = {}) } - public fun transform(clazz: Class, action: Action?) + public fun transform(clazz: Class, action: Action) public fun transform(transformer: T) { - transform(transformer, null) + transform(transformer, action = {}) } - public fun transform(transformer: T, action: Action?) + public fun transform(transformer: T, action: Action) } From fd0570512fee4aab170b85f2d53e0c7718d56c6d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 08:37:29 +0800 Subject: [PATCH 574/941] Update dependency gradle to v9.0.0 (#1557) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e8a90ba69..2a84e188b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-rc-4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 35c4cb2e0500a4c81d4c62544637a6a7d0ee0fcd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 1 Aug 2025 08:38:22 +0800 Subject: [PATCH 575/941] Update plugin android-lint to v8.12.0 (#1558) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c2e334738..a39426822 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,7 +35,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.11.1" +android-lint = "com.android.lint:8.12.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From 39d1869c243e366053cd0904701a3849d6e26252 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 1 Aug 2025 09:13:18 +0800 Subject: [PATCH 576/941] Prepare version 9.0.0-rc3 --- docs/changes/README.md | 9 ++++++++- gradle.properties | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 540325ddd..56798fe88 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,14 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc2...HEAD) - 2025-xx-xx +## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 + +!!! warning + + This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing + significant improvements to maintainability, performance, and future extensibility. It introduces many new features, + enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult + the [new doc site](https://gradleup.com/shadow/) before upgrading. ### Added diff --git a/gradle.properties b/gradle.properties index f4a1af2be..2af32ae99 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0-rc3 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 7343c2040e74cedd3727436099e7041279fa0e3a Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 1 Aug 2025 09:14:22 +0800 Subject: [PATCH 577/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 56798fe88..d776dee89 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc3...HEAD) - 2025-xx-xx + + ## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 !!! warning diff --git a/gradle.properties b/gradle.properties index 2af32ae99..f4a1af2be 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-rc3 +VERSION_NAME=9.0.0-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From e8514de3865ca80a7d9d0da49bd4fa55317b7974 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 1 Aug 2025 10:06:00 +0800 Subject: [PATCH 578/941] Prepare changelog for version 9 final (#1559) --- docs/changes/README.md | 146 +++++++++++++++++++++++++++++++++++------ 1 file changed, 127 insertions(+), 19 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index d776dee89..558a4c4be 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,20 +1,142 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0-rc3...HEAD) - 2025-xx-xx - - -## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 +## [9.0.0](https://github.com/GradleUp/shadow/compare/9.0.0-rc3...HEAD) - Unreleased !!! warning - This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing + This release is a major update from the 8.x series. The plugin has been fully rewritten in Kotlin, bringing significant improvements to maintainability, performance, and future extensibility. It introduces many new features, enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult the [new doc site](https://gradleup.com/shadow/) before upgrading. ### Added +- Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) +- Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) +- Support configuring `separator` in `AppendingTransformer`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) + This is useful for handling files like `resources/application.yml`. +- Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) +- Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) +- Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) +- Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) +- Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) +- Reduce duplicate `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) +- Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) +- 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)) +- Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) + ``` + --enable-auto-relocation Enables auto relocation of packages in the dependencies. + --no-enable-auto-relocation Disables option --enable-auto-relocation. + --fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate. + --no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries. + --minimize-jar Minimizes the jar by removing unused classes. + --no-minimize-jar Disables option --minimize-jar. + --relocation-prefix Prefix used for auto relocation of packages in the dependencies. + --rerun Causes the task to be re-run even if up-to-date. + ``` +- Support skipping string constant remapping. ([#1401](https://github.com/GradleUp/shadow/pull/1401)) +- Let `assemble` depend on `shadowJar`. ([#1524](https://github.com/GradleUp/shadow/pull/1524)) +- Fail build when inputting AAR files or using Shadow with AGP. ([#1530](https://github.com/GradleUp/shadow/pull/1530)) +- Add `PreserveFirstFoundResourceTransformer`. ([#1548](https://github.com/GradleUp/shadow/pull/1548)) + This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and + want to ensure that only the first found resource is included in the final JAR. +- Fail build if the ZIP entries in the shadowed JAR are duplicate. ([#1552](https://github.com/GradleUp/shadow/pull/1552)) + This feature is controlled by the `shadowJar.failOnDuplicateEntries` property, which is `false` by default. + Related to setting `duplicatesStrategy = DuplicatesStrategy.FAIL` but there are some differences: + - It only checks the entries in the shadowed jar, not the input files. + - It works with setting `duplicatesStrategy` to any value. + - It provides a more strict check before the JAR is created. + +### Changed + +- **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) +- **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) +- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) +- **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) +- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) +- **BREAKING CHANGE:** Some public getters have been updated in `SimpleRelocator`. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) +- **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) + Some public `List` parameters are also changed to `Set`. +- **BREAKING CHANGE:** Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) +- **BREAKING CHANGE:** Bump min Java requirement to 11. ([#1242](https://github.com/GradleUp/shadow/pull/1242)) +- **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) +- **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) +- **BREAKING CHANGE:** Change the default `duplicatesStrategy` from `EXCLUDE` to `INCLUDE`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + - `ShadowJar` recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. + - Now `ShadowJar` honors `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. + - See more details at [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy). +- **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) + In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial + issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip + the files, only copy the files as-is. If you still want to shadow the unzipped files, try out something like: + ```kotlin + tasks.shadowJar { + // Unzip the files before pass them to `from` by using `zipTree`. + from(zipTree(files('path/to/your/file.zip'))) + } + ``` + or + ```kotlin + dependencies { + // Add the files to `implementation` configuration, Shadow will unzip them automatically. + implementation(files('path/to/your/file.zip')) + } + ``` +- **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) + Aims to better align with the name `org.apache.maven.plugins.shade.resource.ResourceTransformer.java` + and to distinguish itself from `org.gradle.api.Transformer.java`. +- **BREAKING CHANGE:** Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) +- **BREAKING CHANGE:** Polish `ShadowSpec`. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) + - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. + - `ShadowSpec` no longer extends `CopySpec`. + - Overload `relocate`, `transform` and things for better usability in Kotlin. +- **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) +- **BREAKING CHANGE:** Rename `ShadowJar`'s `isEnableRelocation` to `enableAutoRelocation`. ([#1541](https://github.com/GradleUp/shadow/pull/1541)) +- Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) +- Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) +- Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) +- Mark `ShadowJar.dependencyFilter` as `@Input`. ([#1206](https://github.com/GradleUp/shadow/pull/1206)) +- Polish `startShadowScripts` task registering. ([#1216](https://github.com/GradleUp/shadow/pull/1216)) +- Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- Migrate doc sites to MkDocs. ([#1302](https://github.com/GradleUp/shadow/pull/1302)) +- `runShadow` no longer depends on `installShadowDist`. ([#1353](https://github.com/GradleUp/shadow/pull/1353)) +- Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) +- In-development snapshots are now published to the Central Portal Snapshots repository. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) +- Expose `AbstractDependencyFilter` from `internal` to `public`. ([#1538](https://github.com/GradleUp/shadow/pull/1538)) + You can access it via `com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter.AbstractDependencyFilter`. +- Mark `Action` parameters as non-null. ([#1555](https://github.com/GradleUp/shadow/pull/1555)) + +### Fixed + +- Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) +- Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) +- Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) +- Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) +- Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) +- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) +- Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) +- Allow using file trees of JARs together with the configuration cache. ([#1441](https://github.com/GradleUp/shadow/pull/1441)) + +### Removed + +- **BREAKING CHANGE:** Some public getters and setters have been removed in `SimpleRelocator`. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) +- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) +- **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) +- **BREAKING CHANGE:** Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) +- **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) +- **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) +- **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) +- **BREAKING CHANGE:** Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) +- **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) + +## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 + +### Added + - Add `PreserveFirstFoundResourceTransformer`. ([#1548](https://github.com/GradleUp/shadow/pull/1548)) This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and want to ensure that only the first found resource is included in the final JAR. @@ -47,13 +169,6 @@ ## [9.0.0-rc2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc2) - 2025-07-23 -!!! warning - - This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing - significant improvements to maintainability, performance, and future extensibility. It introduces many new features, - enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult - the [new doc site](https://gradleup.com/shadow/) before upgrading. - ### Added - Support skipping string constant remapping. ([#1401](https://github.com/GradleUp/shadow/pull/1401)) @@ -73,13 +188,6 @@ ## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 -!!! warning - - This release is a major update from the 8.3.x series. The plugin has been fully rewritten in Kotlin, bringing - significant improvements to maintainability, performance, and future extensibility. It introduces many new features, - enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult - the [new doc site](https://gradleup.com/shadow/) before upgrading. - ### Added - Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) From 583156e09503770fd79538745961023f4952a93e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 1 Aug 2025 11:10:40 +0800 Subject: [PATCH 579/941] Mark @JvmSynthetic to hide callers from Groovy DSL (#1561) --- .../github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index e18fc339e..c10186fc9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -321,6 +321,7 @@ public abstract class ShadowJar : * * This is a convenience method for [relocate] with a reified type parameter for Kotlin. */ + @JvmSynthetic // For Groovy build scripts, hide from normal callers. public inline fun relocate() { relocate(R::class.java, action = {}) } @@ -330,6 +331,7 @@ public abstract class ShadowJar : * * This is a convenience method for [relocate] with a reified type parameter for Kotlin. */ + @JvmSynthetic // For Groovy build scripts, hide from normal callers. public inline fun relocate(action: Action) { relocate(R::class.java, action) } @@ -353,6 +355,7 @@ public abstract class ShadowJar : * * This is a convenience method for [transform] with a reified type parameter for Kotlin. */ + @JvmSynthetic // For Groovy build scripts, hide from normal callers. public inline fun transform() { transform(T::class.java, action = {}) } @@ -362,6 +365,7 @@ public abstract class ShadowJar : * * This is a convenience method for [transform] with a reified type parameter for Kotlin. */ + @JvmSynthetic // For Groovy build scripts, hide from normal callers. public inline fun transform(action: Action) { transform(T::class.java, action) } From 25dbc576154bdfd4be209500220723f956297eb1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 1 Aug 2025 11:52:58 +0800 Subject: [PATCH 580/941] Reduce relocate and transform overloads from Kotlin side (#1562) --- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 28 ++----------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index c10186fc9..4e1ba563e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -318,21 +318,9 @@ public abstract class ShadowJar : /** * Relocate classes and resources using a [Relocator]. - * - * This is a convenience method for [relocate] with a reified type parameter for Kotlin. - */ - @JvmSynthetic // For Groovy build scripts, hide from normal callers. - public inline fun relocate() { - relocate(R::class.java, action = {}) - } - - /** - * Relocate classes and resources using a [Relocator]. - * - * This is a convenience method for [relocate] with a reified type parameter for Kotlin. */ @JvmSynthetic // For Groovy build scripts, hide from normal callers. - public inline fun relocate(action: Action) { + public inline fun relocate(action: Action = Action {}) { relocate(R::class.java, action) } @@ -352,21 +340,9 @@ public abstract class ShadowJar : /** * Transform resources using a [ResourceTransformer]. - * - * This is a convenience method for [transform] with a reified type parameter for Kotlin. - */ - @JvmSynthetic // For Groovy build scripts, hide from normal callers. - public inline fun transform() { - transform(T::class.java, action = {}) - } - - /** - * Transform resources using a [ResourceTransformer]. - * - * This is a convenience method for [transform] with a reified type parameter for Kotlin. */ @JvmSynthetic // For Groovy build scripts, hide from normal callers. - public inline fun transform(action: Action) { + public inline fun transform(action: Action = Action {}) { transform(T::class.java, action) } From a963b0ac7e7367dbf1be5cc9a3d265d996779f75 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 1 Aug 2025 14:35:56 +0800 Subject: [PATCH 581/941] Remove ShadowSpec and reduce overloads (#1560) * Remove `ShadowSpec` * Remove overloads * Mark `@JvmOverloads` * Update changelog * Cleanups --- api/shadow.api | 42 ++++++------- docs/changes/README.md | 1 + .../gradle/plugins/shadow/tasks/ShadowJar.kt | 59 ++++++++---------- .../gradle/plugins/shadow/tasks/ShadowSpec.kt | 60 ------------------- 4 files changed, 42 insertions(+), 120 deletions(-) delete mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt diff --git a/api/shadow.api b/api/shadow.api index dcb7fa435..9a9d30e0c 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -189,9 +189,11 @@ public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAc public final fun getCONSTANT_TIME_FOR_ZIP_ENTRIES ()J } -public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar : org/gradle/api/tasks/bundling/Jar, com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec { +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar : org/gradle/api/tasks/bundling/Jar { public fun ()V + public final fun append (Ljava/lang/String;)V public fun append (Ljava/lang/String;Ljava/lang/String;)V + public static synthetic fun append$default (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar;Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)V protected fun copy ()V protected fun createCopyAction ()Lorg/gradle/api/internal/file/copy/CopyAction; public fun dependencies (Lorg/gradle/api/Action;)V @@ -214,38 +216,28 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getToMinimize ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getTransformers ()Lorg/gradle/api/provider/SetProperty; public fun mergeGroovyExtensionModules ()V - public fun mergeServiceFiles ()V + public final fun mergeServiceFiles ()V public fun mergeServiceFiles (Ljava/lang/String;)V public fun mergeServiceFiles (Lorg/gradle/api/Action;)V - public fun minimize ()V + public static synthetic fun mergeServiceFiles$default (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar;Lorg/gradle/api/Action;ILjava/lang/Object;)V + public final fun minimize ()V public fun minimize (Lorg/gradle/api/Action;)V + public static synthetic fun minimize$default (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar;Lorg/gradle/api/Action;ILjava/lang/Object;)V + public final fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)V public fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;Lorg/gradle/api/Action;)V + public final fun relocate (Ljava/lang/Class;)V public fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)V + public final fun relocate (Ljava/lang/String;Ljava/lang/String;)V public fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)V + public static synthetic fun relocate$default (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar;Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;Lorg/gradle/api/Action;ILjava/lang/Object;)V + public static synthetic fun relocate$default (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar;Ljava/lang/Class;Lorg/gradle/api/Action;ILjava/lang/Object;)V + public static synthetic fun relocate$default (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar;Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;ILjava/lang/Object;)V + public final fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;Lorg/gradle/api/Action;)V + public final fun transform (Ljava/lang/Class;)V public fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)V -} - -public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec { - public fun append (Ljava/lang/String;)V - public abstract fun append (Ljava/lang/String;Ljava/lang/String;)V - public abstract fun dependencies (Lorg/gradle/api/Action;)V - public abstract fun mergeGroovyExtensionModules ()V - public abstract fun mergeServiceFiles ()V - public abstract fun mergeServiceFiles (Ljava/lang/String;)V - public abstract fun mergeServiceFiles (Lorg/gradle/api/Action;)V - public abstract fun minimize ()V - public abstract fun minimize (Lorg/gradle/api/Action;)V - public fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;)V - public abstract fun relocate (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;Lorg/gradle/api/Action;)V - public fun relocate (Ljava/lang/Class;)V - public abstract fun relocate (Ljava/lang/Class;Lorg/gradle/api/Action;)V - public fun relocate (Ljava/lang/String;Ljava/lang/String;)V - public abstract fun relocate (Ljava/lang/String;Ljava/lang/String;Lorg/gradle/api/Action;)V - public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;)V - public abstract fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;Lorg/gradle/api/Action;)V - public fun transform (Ljava/lang/Class;)V - public abstract fun transform (Ljava/lang/Class;Lorg/gradle/api/Action;)V + public static synthetic fun transform$default (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar;Lcom/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer;Lorg/gradle/api/Action;ILjava/lang/Object;)V + public static synthetic fun transform$default (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar;Ljava/lang/Class;Lorg/gradle/api/Action;ILjava/lang/Object;)V } public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { diff --git a/docs/changes/README.md b/docs/changes/README.md index 558a4c4be..27f19abe6 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -132,6 +132,7 @@ - **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - **BREAKING CHANGE:** Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) - **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) +- **BREAKING CHANGE:** Remove `ShadowSpec`. ([#1560](https://github.com/GradleUp/shadow/pull/1560)) ## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 4e1ba563e..da91e03fb 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -57,9 +57,7 @@ import org.gradle.api.tasks.bundling.ZipEntryCompression import org.gradle.api.tasks.options.Option @CacheableTask -public abstract class ShadowJar : - Jar(), - ShadowSpec { +public abstract class ShadowJar : Jar() { private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) init { @@ -221,55 +219,41 @@ public abstract class ShadowJar : @get:Inject protected abstract val archiveOperations: ArchiveOperations - /** - * Enable [minimizeJar], this equals to `minimizeJar.set(true)`. - */ - override fun minimize() { - minimizeJar.set(true) - } - /** * Enable [minimizeJar] and execute the [action] with the [DependencyFilter] for minimize. */ - override fun minimize(action: Action) { - minimize() + @JvmOverloads + public open fun minimize(action: Action = Action {}) { + minimizeJar.set(true) action.execute(dependencyFilterForMinimize) } /** * Extra dependency operations to be applied in the shadow steps. */ - override fun dependencies(action: Action) { + public open fun dependencies(action: Action) { action.execute(dependencyFilter.get()) } - /** - * Merge Java services files. - */ - override fun mergeServiceFiles() { - transform(ServiceFileTransformer::class.java, action = {}) - } - /** * Merge Java services files with [rootPath]. */ - override fun mergeServiceFiles(rootPath: String) { - transform(ServiceFileTransformer::class.java) { - it.path = rootPath - } + public open fun mergeServiceFiles(rootPath: String) { + mergeServiceFiles { it.path = rootPath } } /** * Merge Java services files with [action]. */ - override fun mergeServiceFiles(action: Action) { + @JvmOverloads + public open fun mergeServiceFiles(action: Action = Action {}) { transform(ServiceFileTransformer::class.java, action) } /** * Merge Groovy extension modules (`META-INF/**/org.codehaus.groovy.runtime.ExtensionModule`). */ - override fun mergeGroovyExtensionModules() { + public open fun mergeGroovyExtensionModules() { transform(GroovyExtensionModuleTransformer::class.java, action = {}) } @@ -279,10 +263,10 @@ public abstract class ShadowJar : * e.g. `append("resources/application.yml", "\n---\n")` for merging `resources/application.yml` files. * * @param resourcePath The path to the resource in the jar. - * @param separator The separator to use between the original content and the appended content, - * defaults to `\n` ([AppendingTransformer.DEFAULT_SEPARATOR]). + * @param separator The separator to use between the original content and the appended content, defaults to [AppendingTransformer.DEFAULT_SEPARATOR] (`\n`). */ - override fun append(resourcePath: String, separator: String) { + @JvmOverloads + public open fun append(resourcePath: String, separator: String = AppendingTransformer.DEFAULT_SEPARATOR) { transform(AppendingTransformer::class.java) { it.resource.set(resourcePath) it.separator.set(separator) @@ -292,10 +276,11 @@ public abstract class ShadowJar : /** * Relocate classes and resources matching [pattern] to [destination] using [SimpleRelocator]. */ - override fun relocate( + @JvmOverloads + public open fun relocate( pattern: String, destination: String, - action: Action, + action: Action = Action {}, ) { val relocator = SimpleRelocator(pattern, destination) addRelocator(relocator, action) @@ -304,7 +289,8 @@ public abstract class ShadowJar : /** * Relocate classes and resources using a [Relocator]. */ - override fun relocate(clazz: Class, action: Action) { + @JvmOverloads + public open fun relocate(clazz: Class, action: Action = Action {}) { val relocator = clazz.getDeclaredConstructor().newInstance() addRelocator(relocator, action) } @@ -312,7 +298,8 @@ public abstract class ShadowJar : /** * Relocate classes and resources using a [Relocator]. */ - override fun relocate(relocator: R, action: Action) { + @JvmOverloads + public open fun relocate(relocator: R, action: Action = Action {}) { addRelocator(relocator, action) } @@ -327,14 +314,16 @@ public abstract class ShadowJar : /** * Transform resources using a [ResourceTransformer]. */ - override fun transform(clazz: Class, action: Action) { + @JvmOverloads + public open fun transform(clazz: Class, action: Action = Action {}) { addTransform(clazz.create(objectFactory), action) } /** * Transform resources using a [ResourceTransformer]. */ - override fun transform(transformer: T, action: Action) { + @JvmOverloads + public open fun transform(transformer: T, action: Action = Action {}) { addTransform(transformer, action) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt deleted file mode 100644 index 9c487a5e7..000000000 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowSpec.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.tasks - -import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator -import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator -import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import org.gradle.api.Action - -public interface ShadowSpec { - public fun minimize() - - public fun minimize(action: Action) - - public fun dependencies(action: Action) - - public fun mergeServiceFiles() - - public fun mergeServiceFiles(rootPath: String) - - public fun mergeServiceFiles(action: Action) - - public fun mergeGroovyExtensionModules() - - public fun append(resourcePath: String) { - append(resourcePath, AppendingTransformer.DEFAULT_SEPARATOR) - } - - public fun append(resourcePath: String, separator: String) - - public fun relocate(pattern: String, destination: String) { - relocate(pattern, destination, action = {}) - } - - public fun relocate(pattern: String, destination: String, action: Action) - - public fun relocate(clazz: Class) { - relocate(clazz, action = {}) - } - - public fun relocate(clazz: Class, action: Action) - - public fun relocate(relocator: R) { - relocate(relocator, action = {}) - } - - public fun relocate(relocator: R, action: Action) - - public fun transform(clazz: Class) { - transform(clazz, action = {}) - } - - public fun transform(clazz: Class, action: Action) - - public fun transform(transformer: T) { - transform(transformer, action = {}) - } - - public fun transform(transformer: T, action: Action) -} From 8ce60b2c9647d3c4e2d6855065981bb6ff6ef24e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 1 Aug 2025 14:53:06 +0800 Subject: [PATCH 582/941] Remove Relocator.ROLE (#1563) --- api/shadow.api | 5 ----- docs/changes/README.md | 1 + .../jengelman/gradle/plugins/shadow/relocation/Relocator.kt | 4 ---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 9a9d30e0c..1748e8942 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -113,7 +113,6 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat } public abstract interface class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { - public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator$Companion; public abstract fun applyToSourceContent (Ljava/lang/String;)Ljava/lang/String; public abstract fun canRelocateClass (Ljava/lang/String;)Z public abstract fun canRelocatePath (Ljava/lang/String;)Z @@ -122,10 +121,6 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/reloc public abstract fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; } -public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator$Companion { - public final fun getROLE ()Ljava/lang/String; -} - public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator : com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { public fun ()V public fun (Ljava/lang/String;)V diff --git a/docs/changes/README.md b/docs/changes/README.md index 27f19abe6..00ce1ee2f 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -133,6 +133,7 @@ - **BREAKING CHANGE:** Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) - **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) - **BREAKING CHANGE:** Remove `ShadowSpec`. ([#1560](https://github.com/GradleUp/shadow/pull/1560)) +- **BREAKING CHANGE:** Remove `Relocator.ROLE`. ([#1563](https://github.com/GradleUp/shadow/pull/1563)) ## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt index 50616f83e..424ee4508 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -27,10 +27,6 @@ public interface Relocator { */ @get:Input public val skipStringConstants: Boolean get() = false - - public companion object { - public val ROLE: String = Relocator::class.java.name - } } /** From 4eee1949715fa6987d9472a2fe23c4c33c6ae2e8 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 2 Aug 2025 21:18:16 +0800 Subject: [PATCH 583/941] Document relocating all packages but certain ones (#1565) * Document words reflect `relocateAllPackagesButCertainOne` * Update `relocateAllPackagesButCertainOne` --- docs/configuration/relocation/README.md | 28 +++++++++++++++++++ .../gradle/plugins/shadow/RelocationTest.kt | 2 +- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index a1bb6f3de..4148f0ead 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -93,6 +93,34 @@ expression in `%regex[]` before passing it to `include`/`exclude`. } ``` +It may be desirable to relocate all packages in a Shadow JAR except for a select few. This can be accomplished by +specifying a relocation with an empty string `''` as the pattern to match on all packages. An `exclude` filter can then +be used to prevent relocation of specific packages. + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + relocate("", "my/shadow/prefix") { + exclude("META-INF/**") + // Exclude all JUnit packages. + exclude("junit/**") + } + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + relocate('', 'my/shadow/prefix') { + exclude 'META-INF/**' + // Exclude all JUnit packages. + exclude 'junit/**' + } + } + ``` + ## Skipping Relocation for String Constants If there is a class like: diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 2fa92e2f6..5630b4bce 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -478,7 +478,7 @@ class RelocationTest : BasePluginTest() { val relocateConfig = if (exclude) { """ exclude 'junit/**' - exclude '$manifestEntry' + exclude 'META-INF/**' """.trimIndent() } else { "" From 1119c64f3a268f7ba0de0f466de7e590e98ac54e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 2 Aug 2025 22:10:33 +0800 Subject: [PATCH 584/941] Set default group and description in task's ctor (#1567) --- docs/custom-tasks/README.md | 2 -- docs/publishing/README.md | 2 -- .../github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 3 ++- .../github/jengelman/gradle/plugins/shadow/PublishingTest.kt | 1 - .../jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt | 2 -- .../github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 4 ++++ 6 files changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index e861efc35..762d868da 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -9,7 +9,6 @@ the output. ```kotlin val testShadowJar by tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { - group = LifecycleBasePlugin.BUILD_GROUP description = "Create a combined JAR of project and test dependencies" archiveClassifier = "tests" @@ -33,7 +32,6 @@ the output. ```groovy def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - group = LifecycleBasePlugin.BUILD_GROUP description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' diff --git a/docs/publishing/README.md b/docs/publishing/README.md index a8dff9aad..1c07c40d3 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -185,7 +185,6 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th } val testShadowJar by tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { - group = LifecycleBasePlugin.BUILD_GROUP description = "Create a combined JAR of project and test dependencies" archiveClassifier = "tests" from(sourceSets.test.map { it.output }) @@ -218,7 +217,6 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th } def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - group = LifecycleBasePlugin.BUILD_GROUP description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 195b3cd2d..fac856fc9 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -88,6 +88,8 @@ class JavaPluginTest : BasePluginTest() { // Check self properties. with(shadowTask) { + assertThat(group).isEqualTo(LifecycleBasePlugin.BUILD_GROUP) + assertThat(description).isEqualTo("Create a combined JAR of project and runtime dependencies") assertThat(minimizeJar.get()).isFalse() assertThat(enableAutoRelocation.get()).isFalse() assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) @@ -549,7 +551,6 @@ class JavaPluginTest : BasePluginTest() { testImplementation 'junit:junit:3.8.2' } def $testShadowJarTask = tasks.register('$testShadowJarTask', ${ShadowJar::class.java.name}) { - group = LifecycleBasePlugin.BUILD_GROUP description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index cfdff4110..90d206f83 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -161,7 +161,6 @@ class PublishingTest : BasePluginTest() { publishConfiguration( projectBlock = """ def testShadowJar = tasks.register('testShadowJar', ${ShadowJar::class.java.name}) { - group = LifecycleBasePlugin.BUILD_GROUP description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } 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 860ec6a7d..3e6f91fd5 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 @@ -135,8 +135,6 @@ public abstract class ShadowJavaPlugin @Inject constructor( action: Action, ): TaskProvider { return tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> - task.group = LifecycleBasePlugin.BUILD_GROUP - task.description = "Create a combined JAR of project and runtime dependencies" task.archiveClassifier.set("all") task.exclude( "META-INF/INDEX.LIST", diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index da91e03fb..2475b6169 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -55,12 +55,16 @@ import org.gradle.api.tasks.TaskAction import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.bundling.ZipEntryCompression import org.gradle.api.tasks.options.Option +import org.gradle.language.base.plugins.LifecycleBasePlugin @CacheableTask public abstract class ShadowJar : Jar() { private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) init { + group = LifecycleBasePlugin.BUILD_GROUP + description = "Create a combined JAR of project and runtime dependencies" + // https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L55-L64 duplicatesStrategy = INCLUDE manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) From 3f2e70217f37a7c65f55b9c596bdc5008cce37ce Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 2 Aug 2025 22:41:54 +0800 Subject: [PATCH 585/941] Move extensions of ShadowJar (#1566) Also marks `registerShadowJarCommon` as `internal`. --- api/shadow.api | 10 +++--- gradle/lint-baseline.xml | 14 ++++---- .../gradle/plugins/shadow/BasePluginTest.kt | 2 +- .../gradle/plugins/shadow/JavaPluginTest.kt | 2 +- .../gradle/plugins/shadow/MinimizationTest.kt | 2 +- .../gradle/plugins/shadow/RelocationTest.kt | 2 +- .../plugins/shadow/ShadowApplicationPlugin.kt | 2 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 32 ++----------------- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 2 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 30 +++++++++++++++++ 10 files changed, 51 insertions(+), 47 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 1748e8942..91060ea50 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -41,7 +41,6 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowExtension public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin : org/gradle/api/Plugin { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion; - public static final field SHADOW_JAR_TASK_NAME Ljava/lang/String; public static final field SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME Ljava/lang/String; public fun (Lorg/gradle/api/component/SoftwareComponentFactory;)V public synthetic fun apply (Ljava/lang/Object;)V @@ -50,13 +49,10 @@ 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;Lorg/gradle/api/Action;)Lorg/gradle/api/tasks/TaskProvider; } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin : org/gradle/api/Plugin { @@ -185,6 +181,8 @@ public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAc } public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar : org/gradle/api/tasks/bundling/Jar { + public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar$Companion; + public static final field SHADOW_JAR_TASK_NAME Ljava/lang/String; public fun ()V public final fun append (Ljava/lang/String;)V public fun append (Ljava/lang/String;Ljava/lang/String;)V @@ -235,6 +233,10 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public static synthetic fun transform$default (Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar;Ljava/lang/Class;Lorg/gradle/api/Action;ILjava/lang/Object;)V } +public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar$Companion { + public final fun getShadowJar (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; +} + public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun ()V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index 47b7314db..c5f355666 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -1,5 +1,5 @@ - + @@ -30,7 +30,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -151,7 +151,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -162,7 +162,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -173,7 +173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,7 +184,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> 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 658fa4978..e619e95dd 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 @@ -8,9 +8,9 @@ import assertk.assertions.isEqualTo import assertk.assertions.isNotNull import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME -import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index fac856fc9..7af448500 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -15,7 +15,6 @@ import assertk.assertions.isNotNull import assertk.assertions.isNull import assertk.assertions.isTrue import assertk.assertions.single -import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin.Companion.ENABLE_DEVELOCITY_INTEGRATION_PROPERTY import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey @@ -23,6 +22,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttribute import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsNone diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt index 6813ee539..8caeaf1dd 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt @@ -1,7 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat -import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsNone import com.github.jengelman.gradle.plugins.shadow.util.containsOnly diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 5630b4bce..9bcdfcf17 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -7,9 +7,9 @@ import assertk.assertions.isEmpty import assertk.assertions.isInstanceOf import assertk.assertions.isNotEmpty import assertk.fail -import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.Companion.CONSTANT_TIME_FOR_ZIP_ENTRIES +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.runProcess diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 30d9ebde3..863c91bec 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -1,13 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow -import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.shadowJar import com.github.jengelman.gradle.plugins.shadow.internal.applicationExtension import com.github.jengelman.gradle.plugins.shadow.internal.distributions import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension import com.github.jengelman.gradle.plugins.shadow.internal.javaToolchainService import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shadowJar import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project 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 3e6f91fd5..324eae643 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 @@ -6,9 +6,9 @@ import com.github.jengelman.gradle.plugins.shadow.internal.jar import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension 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 com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.registerShadowJarCommon +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.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 @@ -24,9 +24,6 @@ import org.gradle.api.component.SoftwareComponentFactory import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME -import org.gradle.api.tasks.TaskContainer -import org.gradle.api.tasks.TaskProvider -import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin public abstract class ShadowJavaPlugin @Inject constructor( @@ -121,34 +118,9 @@ public abstract class ShadowJavaPlugin @Inject constructor( } public companion object { - public const val SHADOW_JAR_TASK_NAME: String = "shadowJar" public const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" - public inline val TaskContainer.shadowJar: TaskProvider - get() = named(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) - public inline val ConfigurationContainer.shadowRuntimeElements: NamedDomainObjectProvider get() = named(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) - - @JvmStatic - public fun Project.registerShadowJarCommon( - action: Action, - ): TaskProvider { - return tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> - 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", - ) - @Suppress("EagerGradleConfiguration") // Can't use `named` as the task is optional. - tasks.findByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME)?.dependsOn(task) - action.execute(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 06aa00660..42729cf76 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,8 +1,8 @@ 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.isAtLeastKgpVersion import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.registerShadowJarCommon import org.gradle.api.Plugin import org.gradle.api.Project import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 2475b6169..0a67c0bb3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -28,6 +28,7 @@ import kotlin.reflect.full.hasAnnotation import org.apache.tools.zip.Zip64Mode import org.apache.tools.zip.ZipOutputStream import org.gradle.api.Action +import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.file.ArchiveOperations import org.gradle.api.file.ConfigurableFileCollection @@ -52,6 +53,8 @@ import org.gradle.api.tasks.Optional import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.bundling.ZipEntryCompression import org.gradle.api.tasks.options.Option @@ -440,4 +443,31 @@ public abstract class ShadowJar : Jar() { manifest.attributes[multiReleaseAttributeKey] = true } } + + public companion object { + public const val SHADOW_JAR_TASK_NAME: String = "shadowJar" + + public inline val TaskContainer.shadowJar: TaskProvider + get() = named(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) + + internal fun Project.registerShadowJarCommon( + action: (ShadowJar) -> Unit, + ): TaskProvider { + return tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> + 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", + ) + @Suppress("EagerGradleConfiguration") // Can't use `named` as the task is optional. + tasks.findByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME)?.dependsOn(task) + action(task) + } + } + } } From 4ce116a0d981317c30e1320412d7df87e851a5c0 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 2 Aug 2025 22:56:25 +0800 Subject: [PATCH 586/941] Mark more @JvmSynthetic for extensions (#1568) --- api/shadow.api | 10 +++++----- .../gradle/plugins/shadow/ShadowApplicationPlugin.kt | 2 ++ .../gradle/plugins/shadow/ShadowBasePlugin.kt | 1 + .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 1 + .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 5 +++-- 5 files changed, 12 insertions(+), 7 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 91060ea50..08513a4c3 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -14,8 +14,8 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowApplicati } public final class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion { - public final fun getInstallShadowDist (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; - public final fun getStartShadowScripts (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; + public final synthetic fun getInstallShadowDist (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; + public final synthetic fun getStartShadowScripts (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin : org/gradle/api/Plugin { @@ -31,7 +31,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugi } public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$Companion { - public final fun getShadow (Lorg/gradle/api/artifacts/ConfigurationContainer;)Lorg/gradle/api/NamedDomainObjectProvider; + public final synthetic fun getShadow (Lorg/gradle/api/artifacts/ConfigurationContainer;)Lorg/gradle/api/NamedDomainObjectProvider; } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { @@ -52,7 +52,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugi } public final class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion { - public final fun getShadowRuntimeElements (Lorg/gradle/api/artifacts/ConfigurationContainer;)Lorg/gradle/api/NamedDomainObjectProvider; + public final synthetic 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 { @@ -234,7 +234,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar } public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar$Companion { - public final fun getShadowJar (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; + public final synthetic fun getShadowJar (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; } public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 863c91bec..ee40da2bc 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -148,9 +148,11 @@ public abstract class ShadowApplicationPlugin : Plugin { public const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" public const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" + @get:JvmSynthetic public inline val TaskContainer.startShadowScripts: TaskProvider get() = named(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) + @get:JvmSynthetic public inline val TaskContainer.installShadowDist: TaskProvider get() = named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 4b8066c90..a91eed428 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -27,6 +27,7 @@ public abstract class ShadowBasePlugin : Plugin { public const val COMPONENT_NAME: String = SHADOW public const val DISTRIBUTION_NAME: String = SHADOW + @get:JvmSynthetic public inline val ConfigurationContainer.shadow: NamedDomainObjectProvider get() = named(CONFIGURATION_NAME) } 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 324eae643..019434542 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 @@ -120,6 +120,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( public companion object { public const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" + @get:JvmSynthetic public inline val ConfigurationContainer.shadowRuntimeElements: NamedDomainObjectProvider get() = named(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 0a67c0bb3..59346f1fd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -313,7 +313,7 @@ public abstract class ShadowJar : Jar() { /** * Relocate classes and resources using a [Relocator]. */ - @JvmSynthetic // For Groovy build scripts, hide from normal callers. + @JvmSynthetic public inline fun relocate(action: Action = Action {}) { relocate(R::class.java, action) } @@ -337,7 +337,7 @@ public abstract class ShadowJar : Jar() { /** * Transform resources using a [ResourceTransformer]. */ - @JvmSynthetic // For Groovy build scripts, hide from normal callers. + @JvmSynthetic public inline fun transform(action: Action = Action {}) { transform(T::class.java, action) } @@ -447,6 +447,7 @@ public abstract class ShadowJar : Jar() { public companion object { public const val SHADOW_JAR_TASK_NAME: String = "shadowJar" + @get:JvmSynthetic public inline val TaskContainer.shadowJar: TaskProvider get() = named(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) From 9d44715d3502599706c4cb7777c81e90fa6d5d58 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 3 Aug 2025 10:23:22 +0800 Subject: [PATCH 587/941] Run all doc test tasks depend on Jar types (#1570) * Run with `assembleDependsOn` * Fix `$ktorVersion` * Match all `Jar` types --- docs/kotlin-plugins/README.md | 4 ++-- .../plugins/shadow/snippet/GroovyBuildExecutable.kt | 6 ++++++ .../plugins/shadow/snippet/KotlinBuildExecutable.kt | 6 ++++++ .../plugins/shadow/snippet/SnippetExecutable.kt | 11 ++++++++++- 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index 250e2c401..035c38d04 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -127,12 +127,12 @@ automatically configure additional tasks for bundling the shadowed JAR for its ` sourceSets { commonMain { dependencies { - implementation 'io.ktor:ktor-client-core:$ktorVersion' + implementation "io.ktor:ktor-client-core:$ktorVersion" } } jvmMain { dependencies { - implementation 'io.ktor:ktor-client-okhttp:$ktorVersion' + implementation "io.ktor:ktor-client-okhttp:$ktorVersion" } } } diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt index 3982579d8..044d312e7 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt @@ -16,4 +16,10 @@ class GroovyBuildExecutable( id 'com.gradleup.shadow' } """.trimIndent() + + override val assembleDependsOn: String = """ + tasks.named('assemble') { + dependsOn tasks.withType(Jar) // ShadowJar is a subtype of Jar. + } + """.trimIndent() } diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt index 3dd74cdcb..43e04aabc 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt @@ -16,4 +16,10 @@ class KotlinBuildExecutable( id("com.gradleup.shadow") } """.trimIndent() + + override val assembleDependsOn: String = """ + tasks.named("assemble") { + dependsOn(tasks.withType(Jar::class.java)) // ShadowJar is a subtype of Jar. + } + """.trimIndent() } diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index c8d3434ec..d132d76cf 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -14,6 +14,7 @@ sealed class SnippetExecutable : Executable { abstract val lang: DslLang abstract val buildScriptName: String abstract val pluginsBlock: String + abstract val assembleDependsOn: String abstract val snippet: String abstract val displayName: String @@ -46,7 +47,13 @@ sealed class SnippetExecutable : Executable { enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS' """.trimIndent(), ) - projectRoot.addSubProject("api", pluginsBlock) + + val apiScript = buildString { + append(pluginsBlock) + append(lineSeparator()) + append(assembleDependsOn) + } + projectRoot.addSubProject("api", apiScript) val (imports, withoutImports) = importsExtractor(snippet) val mainScript = buildString { @@ -63,6 +70,8 @@ sealed class SnippetExecutable : Executable { append(withoutImports) } append(lineSeparator()) + append(assembleDependsOn) + append(lineSeparator()) }.trimIndent() projectRoot.addSubProject("main", mainScript) projectRoot.resolve("main/foo.jar").createFile().also { From 24be7e6acd3a5db7a1f8cf9ec8cf9304deb2b2a0 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 3 Aug 2025 10:40:44 +0800 Subject: [PATCH 588/941] Document registering a dependency only shadow JAR (#1569) * Set `archiveClassifier` to `test` * Test `registerCustomShadowJarTaskThatContainsDependenciesOnly` * Update `docs/custom-tasks/README.md` * Update src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Remove task refs --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/custom-tasks/README.md | 35 ++++++++++++-- .../gradle/plugins/shadow/JavaPluginTest.kt | 46 +++++++++++++++++-- 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/docs/custom-tasks/README.md b/docs/custom-tasks/README.md index 762d868da..a9e56b138 100644 --- a/docs/custom-tasks/README.md +++ b/docs/custom-tasks/README.md @@ -11,7 +11,7 @@ the output. val testShadowJar by tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { description = "Create a combined JAR of project and test dependencies" - archiveClassifier = "tests" + archiveClassifier = "test" from(sourceSets.test.map { it.output }) configurations = project.configurations.testRuntimeClasspath.map { listOf(it) } @@ -34,7 +34,7 @@ the output. def testShadowJar = tasks.register('testShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { description = 'Create a combined JAR of project and test dependencies' - archiveClassifier = 'tests' + archiveClassifier = 'test' from sourceSets.named('test').map { it.output } configurations = project.configurations.named('testRuntimeClasspath').map { [it] } @@ -53,7 +53,36 @@ the output. The code snippet above will generate a shadowed JAR containing both the `main` and `test` sources as well as all `testRuntimeOnly` and `testImplementation` dependencies. The file is output to -`build/libs/--tests.jar`. +`build/libs/--test.jar`. + +## Creating a Dependencies-Only Shadow JAR + +It is also possible to create a shadow JAR that contains *only* the dependencies and none of the project's own +source code. This is accomplished by creating a custom [`ShadowJar`][ShadowJar] task and configuring the +[`configurations`][ShadowJar.configurations] property, but **not** adding any project sources with `from(...)`. + +=== "Kotlin" + + ```kotlin + tasks.registering(com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar::class) { + description = "Create a shadow JAR of all dependencies" + archiveClassifier = "dep" + configurations = project.configurations.runtimeClasspath.map { listOf(it) } + } + ``` + +=== "Groovy" + + ```groovy + tasks.register('dependencyShadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + description = 'Create a shadow JAR of all dependencies' + archiveClassifier = 'dep' + configurations = project.configurations.named('runtimeClasspath').map { [it] } + } + ``` + +The above configuration will create a shadow JAR file that contains only the classes from the `runtimeClasspath` +configuration. The standard `jar` task will still produce a JAR with only the project's sources. [Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 7af448500..9cb826eba 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -552,7 +552,7 @@ class JavaPluginTest : BasePluginTest() { } def $testShadowJarTask = tasks.register('$testShadowJarTask', ${ShadowJar::class.java.name}) { description = 'Create a combined JAR of project and test dependencies' - archiveClassifier = 'tests' + archiveClassifier = 'test' from sourceSets.named('test').map { it.output } configurations = project.configurations.named('testRuntimeClasspath').map { [it] } manifest { @@ -564,7 +564,7 @@ class JavaPluginTest : BasePluginTest() { run(testShadowJarTask) - assertThat(jarPath("build/libs/my-1.0-tests.jar")).useAll { + assertThat(jarPath("build/libs/my-1.0-test.jar")).useAll { containsOnly( "my/", mainClassEntry, @@ -574,7 +574,7 @@ class JavaPluginTest : BasePluginTest() { getMainAttr(mainClassAttributeKey).isNotNull() } - val pathString = path("build/libs/my-1.0-tests.jar").toString() + val pathString = path("build/libs/my-1.0-test.jar").toString() val runningOutput = runProcess("java", "-jar", pathString, "foo") assertThat(runningOutput).contains( "Hello, World! (foo) from Main", @@ -582,6 +582,46 @@ class JavaPluginTest : BasePluginTest() { ) } + @Issue( + "https://github.com/GradleUp/shadow/issues/443", + ) + @Test + fun registerCustomShadowJarTaskThatContainsDependenciesOnly() { + val mainClassEntry = writeClass() + val dependencyShadowJar = "dependencyShadowJar" + + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + def $dependencyShadowJar = tasks.register('$dependencyShadowJar', ${ShadowJar::class.java.name}) { + description = 'Create a shadow JAR of all dependencies' + archiveClassifier = 'dep' + configurations = project.configurations.named('runtimeClasspath').map { [it] } + } + """.trimIndent(), + ) + + run("jar", dependencyShadowJar) + + assertThat(jarPath("build/libs/my-1.0.jar")).useAll { + containsOnly( + "my/", + mainClassEntry, + *manifestEntries, + ) + transform { it.mainAttrSize }.isEqualTo(1) + } + assertThat(jarPath("build/libs/my-1.0-dep.jar")).useAll { + containsOnly( + *junitEntries, + *manifestEntries, + ) + transform { it.mainAttrSize }.isEqualTo(1) + } + } + @Issue( "https://github.com/GradleUp/shadow/issues/915", ) From b54aea09851b7184bbf8bcd8d35a257f9be5cb15 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 3 Aug 2025 11:04:36 +0800 Subject: [PATCH 589/941] Shorten lineSeparator (#1571) --- .../plugins/shadow/snippet/SnippetExecutable.kt | 15 ++++++++------- .../plugins/shadow/ApplicationPluginTest.kt | 2 +- .../gradle/plugins/shadow/BasePluginTest.kt | 16 +++++++++------- .../gradle/plugins/shadow/FilteringTest.kt | 2 +- .../gradle/plugins/shadow/JavaPluginTest.kt | 6 +++--- .../gradle/plugins/shadow/MinimizationTest.kt | 8 ++++---- .../gradle/plugins/shadow/PublishingTest.kt | 12 ++++++------ .../shadow/caching/FilteringCachingTest.kt | 10 +++++----- .../shadow/caching/RelocationCachingTest.kt | 2 +- .../shadow/caching/ShadowJarCachingTest.kt | 6 +++--- .../shadow/caching/TransformerCachingTest.kt | 4 ++-- .../transformers/AppendingTransformerTest.kt | 2 +- .../transformers/ServiceFileTransformerTest.kt | 2 +- .../shadow/util/AppendableMavenRepository.kt | 8 +++++--- 14 files changed, 50 insertions(+), 45 deletions(-) diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index d132d76cf..d684c52a5 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -1,6 +1,5 @@ package com.github.jengelman.gradle.plugins.shadow.snippet -import java.lang.System.lineSeparator import java.nio.file.Path import java.util.jar.JarOutputStream import kotlin.io.path.createDirectory @@ -50,7 +49,7 @@ sealed class SnippetExecutable : Executable { val apiScript = buildString { append(pluginsBlock) - append(lineSeparator()) + append(lineSeparator) append(assembleDependsOn) } projectRoot.addSubProject("api", apiScript) @@ -58,20 +57,20 @@ sealed class SnippetExecutable : Executable { val (imports, withoutImports) = importsExtractor(snippet) val mainScript = buildString { append(imports) - append(lineSeparator()) + append(lineSeparator) // All buildscript {} blocks must appear before any plugins {} blocks in the script. if (withoutImports.contains("buildscript {")) { append(withoutImports) } else { if (!withoutImports.contains("plugins {")) { append(pluginsBlock) - append(lineSeparator()) + append(lineSeparator) } append(withoutImports) } - append(lineSeparator()) + append(lineSeparator) append(assembleDependsOn) - append(lineSeparator()) + append(lineSeparator) }.trimIndent() projectRoot.addSubProject("main", mainScript) projectRoot.resolve("main/foo.jar").createFile().also { @@ -109,7 +108,7 @@ sealed class SnippetExecutable : Executable { snippet.lines().forEach { line -> val target = if (line.trim().startsWith("import ")) imports else withoutImports - target.append(line).append(lineSeparator()) + target.append(line).append(lineSeparator) } return imports.toString() to @@ -121,6 +120,8 @@ sealed class SnippetExecutable : Executable { private val testGradleVersion = System.getProperty("TEST_GRADLE_VERSION") ?: error("TEST_GRADLE_VERSION system property is not set.") + private val lineSeparator = System.lineSeparator() + fun create( lang: DslLang, snippet: String, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 9082e7763..23af00039 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -241,7 +241,7 @@ class ApplicationPluginTest : BasePluginTest() { args 'foo' $runShadowBlock } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) settingsScriptPath.writeText( getDefaultSettingsBuildScript( 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 e619e95dd..0edb0b3ed 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 @@ -123,7 +123,7 @@ abstract class BasePluginTest { } $groupInfo $versionInfo - """.trimIndent() + System.lineSeparator() + """.trimIndent() + lineSeparator } fun getDefaultSettingsBuildScript( @@ -147,7 +147,7 @@ abstract class BasePluginTest { } enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS' $endBlock - """.trimIndent() + System.lineSeparator() + """.trimIndent() + lineSeparator } fun jarPath(relative: String, parent: Path = projectRoot): JarPath { @@ -289,7 +289,7 @@ abstract class BasePluginTest { dependencies { implementation 'junit:junit:3.8.2' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) path("server/src/main/java/server/Server.java").writeText( @@ -308,7 +308,7 @@ abstract class BasePluginTest { $shadowJar { $serverShadowBlock } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) if (!clientShadowed) return @@ -317,7 +317,7 @@ abstract class BasePluginTest { $shadowJar { relocate 'junit.framework', 'client.junit.framework' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) path("server/src/main/java/server/Server.java").writeText( """ @@ -358,7 +358,7 @@ abstract class BasePluginTest { """ ${getDefaultProjectBuildScript("java-gradle-plugin", withGroup = true, withVersion = true)} $gradlePluginBlock - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) path("src/main/java/my/plugin/MyPlugin.java").writeText( @@ -395,6 +395,8 @@ abstract class BasePluginTest { private val testGradleVersion = System.getProperty("TEST_GRADLE_VERSION") ?: error("TEST_GRADLE_VERSION system property is not set.") + val lineSeparator: String = System.lineSeparator() + val testKitDir: Path = run { var gradleUserHome = System.getenv("GRADLE_USER_HOME") if (gradleUserHome == null) { @@ -434,7 +436,7 @@ abstract class BasePluginTest { fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } fun implementationFiles(vararg paths: Path): String { - return paths.joinToString(System.lineSeparator()) { "implementation files('${it.invariantSeparatorsPathString}')" } + return paths.joinToString(lineSeparator) { "implementation files('${it.invariantSeparatorsPathString}')" } } inline fun transform( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 020c825c7..1ddd065a8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -26,7 +26,7 @@ class FilteringTest : BasePluginTest() { implementation 'my:a:1.0' implementation 'my:b:1.0' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 9cb826eba..179ce2021 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -204,7 +204,7 @@ class JavaPluginTest : BasePluginTest() { attributes '$multiReleaseAttributeKey': 'true' } } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) run(serverShadowJarTask) @@ -505,7 +505,7 @@ class JavaPluginTest : BasePluginTest() { projectScriptPath.writeText( """ ${getDefaultProjectBuildScript("java-gradle-plugin")} - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) val outputCompileOnly = dependencies(COMPILE_ONLY_CONFIGURATION_NAME) @@ -525,7 +525,7 @@ class JavaPluginTest : BasePluginTest() { projectScriptPath.writeText( """ ${getDefaultProjectBuildScript("java-gradle-plugin")} - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) val output = dependencies( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt index 8caeaf1dd..0c3c68736 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt @@ -250,7 +250,7 @@ class MinimizationTest : BasePluginTest() { settingsScriptPath.appendText( """ include 'api', 'lib', 'impl' - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) projectScriptPath.writeText("") @@ -271,7 +271,7 @@ class MinimizationTest : BasePluginTest() { plugins { id 'java' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) path("api/src/main/java/api/Entity.java").writeText( @@ -296,7 +296,7 @@ class MinimizationTest : BasePluginTest() { implementation 'junit:junit:3.8.2' implementation project(':lib') } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) path("impl/src/main/java/impl/SimpleEntity.java").writeText( @@ -315,7 +315,7 @@ class MinimizationTest : BasePluginTest() { $shadowJar { minimize() } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 90d206f83..3cd80d973 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -54,7 +54,7 @@ class PublishingTest : BasePluginTest() { @BeforeEach override fun setup() { super.setup() - settingsScriptPath.appendText("rootProject.name = 'maven'" + System.lineSeparator()) + settingsScriptPath.appendText("rootProject.name = 'maven'" + lineSeparator) } @Test @@ -65,7 +65,7 @@ class PublishingTest : BasePluginTest() { archiveClassifier = '' archiveBaseName = 'maven-all' """.trimIndent(), - ) + System.lineSeparator(), + ) + lineSeparator, ) publish() @@ -95,7 +95,7 @@ class PublishingTest : BasePluginTest() { java { toolchain.languageVersion = JavaLanguageVersion.of(17) } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) publish() assertions(attrsWithoutTargetJvm + targetJvmAttr17) @@ -105,7 +105,7 @@ class PublishingTest : BasePluginTest() { java { targetCompatibility = JavaVersion.VERSION_11 } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) publish() assertions(attrsWithoutTargetJvm + targetJvmAttr11) @@ -115,7 +115,7 @@ class PublishingTest : BasePluginTest() { java { sourceCompatibility = JavaVersion.VERSION_1_8 } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) publish() // sourceCompatibility doesn't affect the target JVM version. @@ -126,7 +126,7 @@ class PublishingTest : BasePluginTest() { tasks.named('compileJava') { options.release = 8 } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) publish() // options.release flag is honored. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt index 5da2cd1cd..948e8c375 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt @@ -74,7 +74,7 @@ class FilteringCachingTest : BaseCachingTest() { implementation 'my:a:1.0' implementation 'my:b:1.0' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) assertCompositeExecutions { @@ -92,7 +92,7 @@ class FilteringCachingTest : BaseCachingTest() { $shadowJar { exclude '**.properties' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) assertCompositeExecutions { @@ -109,7 +109,7 @@ class FilteringCachingTest : BaseCachingTest() { $shadowJar { include '$mainClassEntry' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) assertCompositeExecutions { @@ -125,7 +125,7 @@ class FilteringCachingTest : BaseCachingTest() { $shadowJar { include '$main2ClassEntry' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) assertCompositeExecutions { @@ -146,7 +146,7 @@ class FilteringCachingTest : BaseCachingTest() { dependencies { implementation 'junit:junit:3.8.2' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) assertCompositeExecutions { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index 164f7bec6..38a9ee786 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -15,7 +15,7 @@ class RelocationCachingTest : BaseCachingTest() { dependencies { implementation 'junit:junit:3.8.2' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) val mainClassEntry = writeClass(withImports = true) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index 9979080bc..ddb017215 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -32,7 +32,7 @@ class ShadowJarCachingTest : BaseCachingTest() { val replaced = projectScriptPath.readText().lines() .filterNot { it == implementationFiles(artifactBJar) } - .joinToString(System.lineSeparator()) + .joinToString(lineSeparator) projectScriptPath.writeText(replaced) assertCompositeExecutions { @@ -50,7 +50,7 @@ class ShadowJarCachingTest : BaseCachingTest() { dependencies { ${implementationFiles(artifactAJar, artifactBJar)} } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) assertCompositeExecutions { @@ -85,7 +85,7 @@ class ShadowJarCachingTest : BaseCachingTest() { dependencies { implementation 'my:d:1.0' } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) val assertions = { assertCompositeExecutions { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index dadea90e3..2b317ce75 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -145,7 +145,7 @@ class TransformerCachingTest : BaseCachingTest() { $shadowJar { mergeServiceFiles() } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) assertCompositeExecutions() @@ -155,7 +155,7 @@ class TransformerCachingTest : BaseCachingTest() { $shadowJar { mergeGroovyExtensionModules() } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) assertCompositeExecutions() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 55cb546d8..bfebbaf43 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -78,7 +78,7 @@ class AppendingTransformerTest : BaseTransformerTest() { separator = '$APPLICATION_YML_SEPARATOR' """.trimIndent(), ) - block1 + System.lineSeparator() + block2 + block1 + lineSeparator + block2 } projectScriptPath.appendText(config) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 2e20e5099..4b7af4ea6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -276,7 +276,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { duplicatesStrategy = DuplicatesStrategy.$strategy mergeServiceFiles() } - """.trimIndent() + System.lineSeparator(), + """.trimIndent() + lineSeparator, ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index 347ade283..c81e438e4 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -45,7 +45,7 @@ class AppendableMavenRepository( } publishing { publications { - ${modules.joinToString(System.lineSeparator()) { createPublication(it) }} + ${modules.joinToString(lineSeparator) { createPublication(it) }} } repositories { maven { url = '${root.toUri()}' } @@ -62,7 +62,7 @@ class AppendableMavenRepository( val pubName = outputJar.name.replace(".", "") var index = -1 - val nodes = dependencies.joinToString(System.lineSeparator()) { + val nodes = dependencies.joinToString(lineSeparator) { index++ val node = "dependencyNode$index" """ @@ -85,7 +85,7 @@ class AppendableMavenRepository( $nodes } } - """.trimIndent() + System.lineSeparator() + """.trimIndent() + lineSeparator } inner class Module( @@ -131,4 +131,6 @@ class AppendableMavenRepository( } } +private val lineSeparator = System.lineSeparator() + val Dependency.coordinate: String get() = "$groupId:$artifactId:$version" From bd7fd150b791066cb9dba66f8e32e3706e5310d6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 3 Aug 2025 15:40:34 +0800 Subject: [PATCH 590/941] Don't register ShadowJar tasks for multiple KMP JVM targets (#1573) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Test `createShadowJarForMainJvmTarget` * Update `ShadowKmpPlugin` * Assert failures with messages ``` > Configure project : e: ❌ `jvm()` Kotlin Target Already Declared Declaring multiple Kotlin Targets of the same type is not supported. Solution: Please remove the duplicate target declaration. See https://kotl.in/declaring-multiple-targets for more details. ``` --- .../plugins/shadow/KotlinPluginsTest.kt | 40 ++++++++++++++++++- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 7 ++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 9ae2e0b0d..12764b2f9 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -4,6 +4,7 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JvmLang import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast @@ -119,7 +120,7 @@ class KotlinPluginsTest : BasePluginTest() { projectScriptPath.appendText( """ kotlin { - jvm("$jvmTargetName") + jvm('$jvmTargetName') sourceSets { commonMain { dependencies { @@ -209,6 +210,43 @@ class KotlinPluginsTest : BasePluginTest() { } } + @Test + fun registerShadowJarForFirstJvmTarget() { + val jvmTargetName = "newJvm" + projectScriptPath.appendText( + """ + kotlin { + jvm() // Default JVM target. + jvm('$jvmTargetName') + sourceSets { + commonMain { + dependencies { + implementation 'my:a:1.0' + } + } + jvmMain { + dependencies { + implementation 'my:b:1.0' + } + } + ${jvmTargetName}Main { + dependencies { + implementation 'my:c:1.0' + } + } + } + } + """.trimIndent(), + ) + + val result = runWithFailure(shadowJarTask, "--info") + + assertThat(result.output).contains( + "$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: $jvmTargetName", // Logged from Shadow. + "Declaring multiple Kotlin Targets of the same type is not supported.", // Thrown from KGP. + ) + } + private fun compileOnlyStdlib(exclude: Boolean): String { return if (exclude) { // Disable the stdlib dependency added via `implementation`. 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 42729cf76..157626ea4 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 @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.internal.isAtLeastKgpVersion import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.registerShadowJarCommon import org.gradle.api.Plugin import org.gradle.api.Project @@ -14,6 +15,12 @@ public abstract class ShadowKmpPlugin : Plugin { override fun apply(project: Project): Unit = with(project) { extensions.getByType(KotlinMultiplatformExtension::class.java).targets.configureEach { target -> if (target !is KotlinJvmTarget) return@configureEach + @Suppress("EagerGradleConfiguration") + if (tasks.findByName(SHADOW_JAR_TASK_NAME) != null) { + // Declaring multiple Kotlin Targets of the same type is not supported. See https://kotl.in/declaring-multiple-targets for more details. + logger.info("$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: ${target.name}") + return@configureEach + } configureShadowJar(target) } From cadbe210d27894cc7e94ce77216ed99ee4fb880d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 3 Aug 2025 15:54:55 +0800 Subject: [PATCH 591/941] Run all functional tests with --info (#1574) --- .../github/jengelman/gradle/plugins/shadow/BasePluginTest.kt | 4 ++-- .../github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 5 ++--- .../jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt | 2 +- .../github/jengelman/gradle/plugins/shadow/RelocationTest.kt | 2 +- 4 files changed, 6 insertions(+), 7 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 0edb0b3ed..e7e2e4fe2 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 @@ -425,8 +425,8 @@ abstract class BasePluginTest { "--build-cache", "--parallel", "--stacktrace", - // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel - "-Dorg.gradle.configuration-cache.parallel=true", + "--info", // Show logs with info level, we assert some of them in the tests. + "-Dorg.gradle.configuration-cache.parallel=true", // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel ) // TODO: enable this flag for all tests once we have fixed all issues with isolated projects. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 179ce2021..38fc8fbad 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -860,7 +860,6 @@ class JavaPluginTest : BasePluginTest() { IP_ARGUMENT, "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a file instead. - "--info", ) assertThat(result.output).all { @@ -890,7 +889,7 @@ class JavaPluginTest : BasePluginTest() { val result = if (enable) { runWithFailure(shadowJarTask) } else { - run(shadowJarTask, "--info") + run(shadowJarTask) } assertThat(result.output).contains( @@ -914,7 +913,7 @@ class JavaPluginTest : BasePluginTest() { val result = if (enable) { runWithFailure(shadowJarTask, "--fail-on-duplicate-entries") } else { - run(shadowJarTask, "--info") + run(shadowJarTask) } assertThat(result.output).contains( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 12764b2f9..5f86968c9 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -239,7 +239,7 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = runWithFailure(shadowJarTask, "--info") + val result = runWithFailure(shadowJarTask) assertThat(result.output).contains( "$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: $jvmTargetName", // Logged from Shadow. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 9bcdfcf17..9cc28152d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -53,7 +53,7 @@ class RelocationTest : BasePluginTest() { } }.toTypedArray() - val result = run(shadowJarTask, "--info") + val result = run(shadowJarTask) assertThat(outputShadowJar).useAll { containsOnly( From 21623eb9c2d52de7ed2452fb111bfc9a2c55d0f2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 3 Aug 2025 16:18:36 +0800 Subject: [PATCH 592/941] Configure manifest attrs for KMP JVM target artifacts task (#1572) * Configure manifest attrs for `artifactsTask` * Unify Jar types to `org.gradle.api.tasks.bundling.Jar` * Test `setManifestAttrsFromJvmTargetJar` --- .../plugins/shadow/KotlinPluginsTest.kt | 45 ++++++++++++++++--- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 16 +------ .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 3 +- .../plugins/shadow/internal/GradleCompat.kt | 6 --- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 16 +++++++ 5 files changed, 59 insertions(+), 27 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 5f86968c9..e99fdb1cc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -174,7 +174,7 @@ class KotlinPluginsTest : BasePluginTest() { @ParameterizedTest @ValueSource(booleans = [false, true]) - fun canSetMainClassAttribute(useShadowAttr: Boolean) { + fun setMainClassAttributeFromMainRun(useShadowAttr: Boolean) { val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin) val main2ClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin, className = "Main2") val mainClassName = "my.Main" @@ -202,11 +202,7 @@ class KotlinPluginsTest : BasePluginTest() { mainClassEntry, main2ClassEntry, ) - if (useShadowAttr) { - getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) - } else { - getMainAttr("Main-Class").isEqualTo(mainClassName) - } + getMainAttr(mainClassAttributeKey).isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) } } @@ -247,6 +243,43 @@ class KotlinPluginsTest : BasePluginTest() { ) } + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun setManifestAttrsFromJvmTargetJar(useShadowAttr: Boolean) { + val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin) + val main2ClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin, className = "Main2") + val mainClassName = "my.Main" + val main2ClassName = "my.Main2" + val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" + projectScriptPath.appendText( + """ + kotlin { + jvm() + } + tasks.named('jvmJar', Jar) { + manifest { + attributes '$mainClassAttributeKey': '$mainClassName' + } + } + $shadowJar { + manifest { + $mainAttr + } + } + """.trimIndent(), + ) + + run(shadowJarTask) + + assertThat(outputShadowJar).useAll { + containsAtLeast( + mainClassEntry, + main2ClassEntry, + ) + getMainAttr(mainClassAttributeKey).isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) + } + } + private fun compileOnlyStdlib(exclude: Boolean): String { return if (exclude) { // Disable the stdlib dependency added via `implementation`. 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 019434542..1f5e3bd41 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 @@ -1,8 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow -import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey -import com.github.jengelman.gradle.plugins.shadow.internal.jar import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.internal.sourceSets @@ -24,6 +22,7 @@ import org.gradle.api.component.SoftwareComponentFactory import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME +import org.gradle.api.tasks.bundling.Jar import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin public abstract class ShadowJavaPlugin @Inject constructor( @@ -38,18 +37,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( } protected open fun Project.configureShadowJar() { - val jarTask = tasks.jar - val taskProvider = registerShadowJarCommon { task -> - @Suppress("EagerGradleConfiguration") // mergeSpec.from hasn't supported lazy configuration yet. - task.manifest.inheritFrom(jarTask.get().manifest) - val attrProvider = jarTask.map { it.manifest.attributes[classPathAttributeKey]?.toString().orEmpty() } - val files = files(configurations.shadow) - task.doFirst { - if (!files.isEmpty) { - val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } - task.manifest.attributes[classPathAttributeKey] = attrs.joinToString(" ").trim() - } - } + val taskProvider = registerShadowJarCommon(tasks.named("jar", Jar::class.java)) { task -> task.from(sourceSets.named("main").map { it.output }) task.configurations.convention(provider { listOf(runtimeConfiguration) }) } 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 157626ea4..68c09f746 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 @@ -6,6 +6,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHAD import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.registerShadowJarCommon import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.tasks.bundling.Jar import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget @@ -28,7 +29,7 @@ public abstract class ShadowKmpPlugin : Plugin { private fun Project.configureShadowJar(target: KotlinJvmTarget) { val kotlinJvmMain = target.compilations.named("main") - registerShadowJarCommon { task -> + registerShadowJarCommon(tasks.named(target.artifactsTaskName, Jar::class.java)) { task -> task.from(kotlinJvmMain.map { it.output.allOutputs }) task.configurations.convention( provider { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index a5090dbf5..e60a0c681 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -15,9 +15,6 @@ import org.gradle.api.provider.Property import org.gradle.api.provider.Provider import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.SourceSetContainer -import org.gradle.api.tasks.TaskContainer -import org.gradle.api.tasks.TaskProvider -import org.gradle.jvm.tasks.Jar import org.gradle.jvm.toolchain.JavaToolchainService /** @@ -56,9 +53,6 @@ internal fun Project.addBuildScanCustomValues() { } } -internal inline val TaskContainer.jar: TaskProvider - get() = named("jar", Jar::class.java) - internal inline fun ObjectFactory.property( defaultValue: Any? = null, ): Property = property(V::class.java).apply { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 59346f1fd..33c938d88 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -1,10 +1,12 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.DefaultInheritManifest import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker +import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.fileCollection import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.property @@ -452,6 +454,7 @@ public abstract class ShadowJar : Jar() { get() = named(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) internal fun Project.registerShadowJarCommon( + jarTask: TaskProvider, action: (ShadowJar) -> Unit, ): TaskProvider { return tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> @@ -465,8 +468,21 @@ public abstract class ShadowJar : Jar() { "META-INF/versions/**/module-info.class", "module-info.class", ) + + @Suppress("EagerGradleConfiguration") // mergeSpec.from hasn't supported lazy configuration yet. + task.manifest.inheritFrom(jarTask.get().manifest) + val attrProvider = jarTask.map { it.manifest.attributes[classPathAttributeKey]?.toString().orEmpty() } + val files = files(configurations.shadow) + task.doFirst { + if (!files.isEmpty) { + val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } + task.manifest.attributes[classPathAttributeKey] = attrs.joinToString(" ").trim() + } + } + @Suppress("EagerGradleConfiguration") // Can't use `named` as the task is optional. tasks.findByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME)?.dependsOn(task) + action(task) } } From fbce5672b464aba5477864c4a83026663a64bd04 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 3 Aug 2025 17:17:42 +0800 Subject: [PATCH 593/941] Remove redundant cast types (#1575) --- .../jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 015e510a7..4096cfb06 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -255,7 +255,7 @@ public open class ShadowCopyAction( private val ZipOutputStream.entries: List get() { return this::class.java.getDeclaredField("entries").apply { isAccessible = true } - .get(this).cast>() + .get(this).cast() } } } From b7ba92e606483e31e140332ba86d233b3d6d0fd4 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 3 Aug 2025 17:35:35 +0800 Subject: [PATCH 594/941] Revert "Run all functional tests with --info (#1574)" This reverts commit cadbe210d27894cc7e94ce77216ed99ee4fb880d. --- .../jengelman/gradle/plugins/shadow/BasePluginTest.kt | 6 ++++-- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 5 +++-- .../jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt | 2 +- .../jengelman/gradle/plugins/shadow/RelocationTest.kt | 2 +- 4 files changed, 9 insertions(+), 6 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 e7e2e4fe2..5ccd3fb90 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 @@ -425,10 +425,12 @@ abstract class BasePluginTest { "--build-cache", "--parallel", "--stacktrace", - "--info", // Show logs with info level, we assert some of them in the tests. - "-Dorg.gradle.configuration-cache.parallel=true", // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel + // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel + "-Dorg.gradle.configuration-cache.parallel=true", ) + const val INFO_ARGUMENT = "--info" + // TODO: enable this flag for all tests once we have fixed all issues with isolated projects. // See https://github.com/GradleUp/shadow/pull/1139. const val IP_ARGUMENT = "-Dorg.gradle.unsafe.isolated-projects=true" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index 38fc8fbad..f39f9c4bc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -860,6 +860,7 @@ class JavaPluginTest : BasePluginTest() { IP_ARGUMENT, "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a file instead. + INFO_ARGUMENT, ) assertThat(result.output).all { @@ -889,7 +890,7 @@ class JavaPluginTest : BasePluginTest() { val result = if (enable) { runWithFailure(shadowJarTask) } else { - run(shadowJarTask) + run(shadowJarTask, INFO_ARGUMENT) } assertThat(result.output).contains( @@ -913,7 +914,7 @@ class JavaPluginTest : BasePluginTest() { val result = if (enable) { runWithFailure(shadowJarTask, "--fail-on-duplicate-entries") } else { - run(shadowJarTask) + run(shadowJarTask, INFO_ARGUMENT) } assertThat(result.output).contains( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index e99fdb1cc..4d01053f8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -235,7 +235,7 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = runWithFailure(shadowJarTask) + val result = runWithFailure(shadowJarTask, INFO_ARGUMENT) assertThat(result.output).contains( "$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: $jvmTargetName", // Logged from Shadow. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 9cc28152d..efaf83f81 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -53,7 +53,7 @@ class RelocationTest : BasePluginTest() { } }.toTypedArray() - val result = run(shadowJarTask) + val result = run(shadowJarTask, INFO_ARGUMENT) assertThat(outputShadowJar).useAll { containsOnly( From f2ab623b2e0a0937fa94b29f5f4f0de8223c2665 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 3 Aug 2025 18:14:02 +0800 Subject: [PATCH 595/941] Prefer calling convention in ShadowApplicationPlugin (#1577) They are not `convention`ed or `set`ed yet, seems we can call `convention`. https://github.com/gradle/gradle/blob/665a9df021dfbf107a84f176d801394c9ef3aacd/platforms/jvm/language-java/src/main/java/org/gradle/api/tasks/JavaExec.java#L129-L130 --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../gradle/plugins/shadow/ShadowApplicationPlugin.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index ee40da2bc..d3207eb4c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -43,8 +43,8 @@ public abstract class ShadowApplicationPlugin : Plugin { task.classpath = files(tasks.shadowJar) with(applicationExtension) { - task.mainModule.set(mainModule) - task.mainClass.set(mainClass) + task.mainModule.convention(mainModule) + task.mainClass.convention(mainClass) task.jvmArguments.convention(provider { applicationDefaultJvmArgs }) } @@ -67,8 +67,8 @@ public abstract class ShadowApplicationPlugin : Plugin { task.classpath = files(tasks.shadowJar) with(applicationExtension) { - task.mainModule.set(mainModule) - task.mainClass.set(mainClass) + task.mainModule.convention(mainModule) + task.mainClass.convention(mainClass) task.conventionMapping.map("applicationName", ::getApplicationName) task.conventionMapping.map("outputDir") { layout.buildDirectory.dir("scriptsShadow").get().asFile } task.conventionMapping.map("executableDir", ::getExecutableDir) From e8c75bed2daa77841fe2ff0bf2d2182c600b1909 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 4 Aug 2025 10:09:26 +0800 Subject: [PATCH 596/941] Cleanups --- .../gradle/plugins/shadow/ApplicationPluginTest.kt | 2 +- .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 2 +- .../gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 9 +++------ .../com.github.johnrengelman.shadow.properties | 3 ++- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 23af00039..ae8156c3d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -90,7 +90,7 @@ class ApplicationPluginTest : BasePluginTest() { assertThat(unixScript.readText()).contains( "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", - "exec \"\$JAVACMD\" \"\$@\"", + "exec \"\$JAVACMD\" \"$@\"", "DEFAULT_JVM_OPTS='\"--add-opens=java.base/java.lang=ALL-UNNAMED\"'", ) assertThat(winScript.readText()).contains( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 3cd80d973..f7daa3fb6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -54,7 +54,7 @@ class PublishingTest : BasePluginTest() { @BeforeEach override fun setup() { super.setup() - settingsScriptPath.appendText("rootProject.name = 'maven'" + lineSeparator) + settingsScriptPath.appendText("rootProject.name = 'maven'$lineSeparator") } @Test diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 4096cfb06..7ef82afb5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -245,17 +245,14 @@ public open class ShadowCopyAction( public companion object { private val logger = Logging.getLogger(ShadowCopyAction::class.java) + private val ZipOutputStream.entries: List + get() = this::class.java.getDeclaredField("entries").apply { isAccessible = true }.get(this).cast() + /** * A copy of [org.gradle.api.internal.file.archive.ZipEntryConstants.CONSTANT_TIME_FOR_ZIP_ENTRIES]. * * 1980-02-01 00:00:00 (318182400000). */ public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis - - private val ZipOutputStream.entries: List - get() { - return this::class.java.getDeclaredField("entries").apply { isAccessible = true } - .get(this).cast() - } } } diff --git a/src/main/resources/META-INF/gradle-plugins/com.github.johnrengelman.shadow.properties b/src/main/resources/META-INF/gradle-plugins/com.github.johnrengelman.shadow.properties index 811cb77da..7de067f9e 100644 --- a/src/main/resources/META-INF/gradle-plugins/com.github.johnrengelman.shadow.properties +++ b/src/main/resources/META-INF/gradle-plugins/com.github.johnrengelman.shadow.properties @@ -1 +1,2 @@ -implementation-class=com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin \ No newline at end of file +# suppress inspection "UnusedProperty" +implementation-class=com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin From b82c3d06733e212dda6ca2a2f8a75db91be57f98 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 5 Aug 2025 09:54:41 +0800 Subject: [PATCH 597/941] Sync changelog for version 8.3.9 https://github.com/GradleUp/shadow/releases/tag/8.3.9 --- docs/changes/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index 00ce1ee2f..91b0122be 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -560,6 +560,12 @@ - Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) +## [8.3.9](https://github.com/GradleUp/shadow/releases/tag/8.3.9) - 2025-08-05 + +**Changed** + +- Use `BufferedOutputStream` when writing the Zip file to improve performance. ([#1579](https://github.com/GradleUp/shadow/pull/1579)) + ## [8.3.8](https://github.com/GradleUp/shadow/releases/tag/8.3.8) - 2025-07-01 ### Fixed From 32906191ef4429657fb7434847702e1e8ec2ff60 Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Tue, 5 Aug 2025 12:17:03 +1000 Subject: [PATCH 598/941] Use BufferedOutputStream when writing the Zip file to improve performance (#1580) * fix: use buffered writes for performance When not using STORED entries use a BufferedOutputStream to avoid lots of small writes to the file system. Testing this with a 300mb jar build I see the total build time going from 40s to 30s. Note that it is not possible to do this with STORED entries as the implementation requires a RandomAccessFile to update the CRC after write. * Cleanups --------- Co-authored-by: Goooler --- docs/changes/README.md | 1 + .../gradle/plugins/shadow/tasks/ShadowJar.kt | 11 ++++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 91b0122be..90ff559ad 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -107,6 +107,7 @@ - Expose `AbstractDependencyFilter` from `internal` to `public`. ([#1538](https://github.com/GradleUp/shadow/pull/1538)) You can access it via `com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter.AbstractDependencyFilter`. - Mark `Action` parameters as non-null. ([#1555](https://github.com/GradleUp/shadow/pull/1555)) +- Use `BufferedOutputStream` when writing the Zip file to improve performance. ([#1580](https://github.com/GradleUp/shadow/pull/1580)) ### Fixed diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 33c938d88..0a2c7eeb1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -22,7 +22,9 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoun import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer +import java.io.BufferedOutputStream import java.io.File +import java.io.FileOutputStream import java.io.IOException import java.util.jar.JarFile import javax.inject.Inject @@ -371,7 +373,14 @@ public abstract class ShadowJar : Jar() { ZipEntryCompression.STORED -> ZipOutputStream.STORED else -> throw IllegalArgumentException("Unknown Compression type $entryCompression.") } - ZipOutputStream(destination).apply { + val stream = if (entryCompressionMethod == ZipOutputStream.STORED) { + ZipOutputStream(destination) + } else { + // Improve performance by avoiding lots of small writes to the file system. + // It is not possible to do this with STORED entries as the implementation requires a RandomAccessFile to update the CRC after write. + ZipOutputStream(destination.outputStream().buffered()) + } + stream.apply { setUseZip64(if (isZip64) Zip64Mode.AsNeeded else Zip64Mode.Never) setMethod(entryCompressionMethod) } From f709dfa3de781df2f0cb37f6868b86d6ba0d7f73 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 5 Aug 2025 10:20:21 +0800 Subject: [PATCH 599/941] Remove imports --- .../github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 0a2c7eeb1..50d54fcac 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -22,9 +22,7 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoun import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import java.io.BufferedOutputStream import java.io.File -import java.io.FileOutputStream import java.io.IOException import java.util.jar.JarFile import javax.inject.Inject From 22754d809cb456a5034728606dedf8891534af68 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 5 Aug 2025 10:25:38 +0800 Subject: [PATCH 600/941] Remove extra type check for ZipEntryCompression (#1581) `ZipEntryCompression` is an enum, there are only `DEFLATED` and `STORED` in it. --- .../github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 50d54fcac..0dfd6dc93 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -369,7 +369,6 @@ public abstract class ShadowJar : Jar() { val entryCompressionMethod = when (entryCompression) { ZipEntryCompression.DEFLATED -> ZipOutputStream.DEFLATED ZipEntryCompression.STORED -> ZipOutputStream.STORED - else -> throw IllegalArgumentException("Unknown Compression type $entryCompression.") } val stream = if (entryCompressionMethod == ZipOutputStream.STORED) { ZipOutputStream(destination) From e9aafe51d79cb9b7304f1ec34d369f99dca0deac Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 5 Aug 2025 13:35:39 +0800 Subject: [PATCH 601/941] Note using Diffuse in the changelog (#1582) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index 90ff559ad..05a7c826c 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -10,6 +10,11 @@ enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult the [new doc site](https://gradleup.com/shadow/) before upgrading. +!!! info + + You can diff the shadowed JARs when upgrading from 8.x to 9.x by using [Diffuse](https://github.com/JakeWharton/diffuse). + If there are any things missing in the changelog or the doc site, please report them to us. + ### Added - Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) From aefb7bcf2a7b1594df631c62cd712c7b6e648d87 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 5 Aug 2025 14:33:54 +0800 Subject: [PATCH 602/941] Update tests for issue 606 (#1583) * Test `canDisableRelocateStringConstantsInSwitchCase` * Revert "Test `canDisableRelocateStringConstantsInSwitchCase`" This reverts commit bc160164b1cb5a49b20e56f1420c3ed2351f3816. * Update `relocateStringConstantsByDefault` and `canDisableRelocateStringConstants` * Comment --- .../gradle/plugins/shadow/RelocationTest.kt | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index efaf83f81..0596ae19a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -592,7 +592,7 @@ class RelocationTest : BasePluginTest() { manifest { attributes '$mainClassAttributeKey': 'my.Main' } - relocate('junit', 'foo.junit') + relocate('foo', 'shadow.foo') } """.trimIndent(), ) @@ -601,12 +601,14 @@ class RelocationTest : BasePluginTest() { val result = runProcess("java", "-jar", outputShadowJar.use { it.toString() }) assertThat(result).contains( - "foo.junit.framework.Test", + "shadow.foo.Foo", + "shadow.foo.Bar", ) } @Issue( "https://github.com/GradleUp/shadow/issues/232", + "https://github.com/GradleUp/shadow/issues/606", ) @ParameterizedTest @ValueSource(booleans = [false, true]) @@ -618,7 +620,7 @@ class RelocationTest : BasePluginTest() { manifest { attributes '$mainClassAttributeKey': 'my.Main' } - relocate('junit', 'foo.junit') { + relocate('foo', 'shadow.foo') { skipStringConstants = $skipStringConstants } } @@ -628,8 +630,17 @@ class RelocationTest : BasePluginTest() { run(shadowJarTask) val result = runProcess("java", "-jar", outputShadowJar.use { it.toString() }) - val expected = if (skipStringConstants) "junit.framework.Test" else "foo.junit.framework.Test" - assertThat(result).contains(expected) + if (skipStringConstants) { + assertThat(result).contains( + "foo.Foo", + "foo.Bar", + ) + } else { + assertThat(result).contains( + "shadow.foo.Foo", + "shadow.foo.Bar", + ) + } } private fun writeClassWithStringRef() { @@ -638,7 +649,12 @@ class RelocationTest : BasePluginTest() { package my; public class Main { public static void main(String[] args) { - System.out.println("junit.framework.Test"); + switch (1) { + default: + System.out.println("foo.Foo"); // Test case for string constants used in switch statements. + break; + } + System.out.println("foo.Bar"); } } """.trimIndent() From 90303bd6b05008f806ecbe426f136d05c8691066 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 08:32:04 +0800 Subject: [PATCH 603/941] Document unzipping with zipTree (#1587) --- docs/configuration/dependencies/README.md | 25 +++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index 19e06bdd2..d9ccc254a 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -68,6 +68,30 @@ method can be used to add extra files. } ``` +Someone may need the unzipped `bar.jar` to be bundled, try out [`zipTree`][Project.zipTree] + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + from(zipTree("bar.jar")) { + // Copy unzipped bar.jar files into META-INF/ in the shadowed JAR. + into("META-INF") + } + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + from(zipTree('bar.jar')) { + // Copy unzipped bar.jar files into META-INF/ in the shadowed JAR. + into('META-INF') + } + } + ``` + See also [Adding Extra Files](../README.md#adding-extra-files) ## Filtering Dependencies @@ -349,3 +373,4 @@ block provides a method that accepts a `Closure` for selecting dependencies. [ShadowJar.dependencies]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html [ShadowJar]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html [Project.configurations]: https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:configurations +[Project.zipTree]: https://docs.gradle.org/current/dsl/org.gradle.api.Project.html#org.gradle.api.Project:zipTree(java.lang.Object) From 68c7d3d8db750fbc447606c1216c995ff86a1ca7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 08:36:52 +0800 Subject: [PATCH 604/941] Remove deprecated ShadowExtension.component (#1586) --- api/shadow.api | 4 +--- docs/changes/README.md | 1 + .../gradle/plugins/shadow/ShadowBasePlugin.kt | 2 +- .../gradle/plugins/shadow/ShadowExtension.kt | 12 +----------- 4 files changed, 4 insertions(+), 15 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 08513a4c3..1a9088fc8 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -34,9 +34,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$C public final synthetic fun getShadow (Lorg/gradle/api/artifacts/ConfigurationContainer;)Lorg/gradle/api/NamedDomainObjectProvider; } -public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { - public fun (Lorg/gradle/api/Project;)V - public final fun component (Lorg/gradle/api/publish/maven/MavenPublication;)V +public abstract interface class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin : org/gradle/api/Plugin { diff --git a/docs/changes/README.md b/docs/changes/README.md index 05a7c826c..6e2592b28 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -140,6 +140,7 @@ - **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) - **BREAKING CHANGE:** Remove `ShadowSpec`. ([#1560](https://github.com/GradleUp/shadow/pull/1560)) - **BREAKING CHANGE:** Remove `Relocator.ROLE`. ([#1563](https://github.com/GradleUp/shadow/pull/1563)) +- **BREAKING CHANGE:** Remove deprecated `ShadowExtension.component`. ([#1586](https://github.com/GradleUp/shadow/pull/1586)) ## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index a91eed428..50529622d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -15,7 +15,7 @@ public abstract class ShadowBasePlugin : Plugin { throw GradleException("This version of Shadow supports Gradle 8.11+ only. Please upgrade.") } @Suppress("DEPRECATION") - extensions.create(EXTENSION_NAME, ShadowExtension::class.java, project) + extensions.create(EXTENSION_NAME, ShadowExtension::class.java) @Suppress("EagerGradleConfiguration") // this should be created eagerly. configurations.create(CONFIGURATION_NAME) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index eb626073b..96b267034 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -1,14 +1,4 @@ package com.github.jengelman.gradle.plugins.shadow -import org.gradle.api.Project -import org.gradle.api.publish.maven.MavenPublication - @Deprecated("This is deprecated since 8.3.2") -public abstract class ShadowExtension(project: Project) { - private val components = project.components - - @Deprecated("Configure publication using `components.shadow` directly.") - public fun component(publication: MavenPublication) { - publication.from(components.findByName(ShadowBasePlugin.COMPONENT_NAME)) - } -} +public interface ShadowExtension From b96fde939c0dfbda1cb2d9177907310e04d1a41e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 09:01:10 +0800 Subject: [PATCH 605/941] Undeprecate ShadowExtension (#1588) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt | 1 - .../github/jengelman/gradle/plugins/shadow/ShadowExtension.kt | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 50529622d..8baab3a9e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -14,7 +14,6 @@ public abstract class ShadowBasePlugin : Plugin { if (GradleVersion.current() < GradleVersion.version("8.11")) { throw GradleException("This version of Shadow supports Gradle 8.11+ only. Please upgrade.") } - @Suppress("DEPRECATION") extensions.create(EXTENSION_NAME, ShadowExtension::class.java) @Suppress("EagerGradleConfiguration") // this should be created eagerly. configurations.create(CONFIGURATION_NAME) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index 96b267034..c20749974 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -1,4 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow -@Deprecated("This is deprecated since 8.3.2") +/** + * TODO: haven't removed this as I want to address issues like https://github.com/GradleUp/shadow/issues/651 in it. + */ public interface ShadowExtension From 6865021be61e4dccee9cb1154e4ba7db08d44343 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 12:10:49 +0800 Subject: [PATCH 606/941] It should be dist instead of dists (#1590) --- .../gradle/plugins/shadow/ShadowApplicationPlugin.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index d3207eb4c..d606138d5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -109,18 +109,18 @@ public abstract class ShadowApplicationPlugin : Plugin { } protected open fun Project.configureDistribution() { - distributions.register(ShadowBasePlugin.DISTRIBUTION_NAME) { distributions -> - distributions.contents { distSpec -> - distSpec.from(file("src/dist")) - distSpec.into("lib") { lib -> + distributions.register(ShadowBasePlugin.DISTRIBUTION_NAME) { + it.contents { shadowDist -> + shadowDist.from(file("src/dist")) + shadowDist.into("lib") { lib -> lib.from(tasks.shadowJar) lib.from(configurations.shadow) } - distSpec.into("bin") { bin -> + shadowDist.into("bin") { bin -> bin.from(tasks.startShadowScripts) bin.filePermissions { it.unix(UNIX_SCRIPT_PERMISSIONS) } } - distSpec.with(applicationExtension.applicationDistribution) + shadowDist.with(applicationExtension.applicationDistribution) } } } From 903f43883b6dce562e5016326fce3f402b71ae96 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 13:00:03 +0800 Subject: [PATCH 607/941] Move const values (#1589) * Move `DISTRIBUTION_NAME` and `COMPONENT_NAME` * Note `ShadowBasePlugin.SHADOW` * Update changelog --- api/shadow.api | 4 ++-- docs/changes/README.md | 2 ++ .../plugins/shadow/ApplicationPluginTest.kt | 2 +- .../plugins/shadow/ShadowApplicationPlugin.kt | 5 ++++- .../gradle/plugins/shadow/ShadowBasePlugin.kt | 20 +++++++++++++++++-- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 4 +++- 6 files changed, 30 insertions(+), 7 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 1a9088fc8..e2b278bb2 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -1,5 +1,6 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin : org/gradle/api/Plugin { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion; + public static final field DISTRIBUTION_NAME Ljava/lang/String; public static final field SHADOW_INSTALL_TASK_NAME Ljava/lang/String; public static final field SHADOW_RUN_TASK_NAME Ljava/lang/String; public static final field SHADOW_SCRIPTS_TASK_NAME Ljava/lang/String; @@ -19,10 +20,8 @@ public final class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationP } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin : org/gradle/api/Plugin { - public static final field COMPONENT_NAME Ljava/lang/String; public static final field CONFIGURATION_NAME Ljava/lang/String; public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$Companion; - public static final field DISTRIBUTION_NAME Ljava/lang/String; public static final field EXTENSION_NAME Ljava/lang/String; public static final field SHADOW Ljava/lang/String; public fun ()V @@ -38,6 +37,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/Shado } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin : org/gradle/api/Plugin { + public static final field COMPONENT_NAME Ljava/lang/String; public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin$Companion; public static final field SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME Ljava/lang/String; public fun (Lorg/gradle/api/component/SoftwareComponentFactory;)V diff --git a/docs/changes/README.md b/docs/changes/README.md index 6e2592b28..cbbe26fee 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -99,6 +99,8 @@ - Overload `relocate`, `transform` and things for better usability in Kotlin. - **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) - **BREAKING CHANGE:** Rename `ShadowJar`'s `isEnableRelocation` to `enableAutoRelocation`. ([#1541](https://github.com/GradleUp/shadow/pull/1541)) +- **BREAKING CHANGE:** Some const values in `ShadowBasePlugin` and `ShadowJavaPlugin` are moved. ([#1589](https://github.com/GradleUp/shadow/pull/1589)) + You can find them in `ShadowJar`, `ShadowApplicationPlugin`, and `ShadowJavaPlugin`. - Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) - Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) - Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index ae8156c3d..bfa36b438 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -6,7 +6,7 @@ import assertk.assertions.contains import assertk.assertions.containsOnly import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo -import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.DISTRIBUTION_NAME +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.DISTRIBUTION_NAME import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.util.Issue diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index d606138d5..e6e30d304 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.SHADOW import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow import com.github.jengelman.gradle.plugins.shadow.internal.applicationExtension import com.github.jengelman.gradle.plugins.shadow.internal.distributions @@ -109,7 +110,7 @@ public abstract class ShadowApplicationPlugin : Plugin { } protected open fun Project.configureDistribution() { - distributions.register(ShadowBasePlugin.DISTRIBUTION_NAME) { + distributions.register(DISTRIBUTION_NAME) { it.contents { shadowDist -> shadowDist.from(file("src/dist")) shadowDist.into("lib") { lib -> @@ -144,6 +145,8 @@ public abstract class ShadowApplicationPlugin : Plugin { */ private const val UNIX_SCRIPT_PERMISSIONS = "rwxr-xr-x" + public const val DISTRIBUTION_NAME: String = SHADOW + public const val SHADOW_RUN_TASK_NAME: String = "runShadow" public const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" public const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 8baab3a9e..7292e981b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -6,6 +6,9 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.component.SoftwareComponentContainer +import org.gradle.api.distribution.DistributionContainer +import org.gradle.api.plugins.ExtensionContainer import org.gradle.util.GradleVersion public abstract class ShadowBasePlugin : Plugin { @@ -20,11 +23,24 @@ public abstract class ShadowBasePlugin : Plugin { } public companion object { + /** + * Most of the components registered by Shadow plugin will use this name (`shadow`). + * + * - [ExtensionContainer.create] + * - [ConfigurationContainer.register] + * - [SoftwareComponentContainer.register] + * - [DistributionContainer.register] + * + * and so on. + * + * @see [EXTENSION_NAME] + * @see [CONFIGURATION_NAME] + * @see [ShadowApplicationPlugin.DISTRIBUTION_NAME] + * @see [ShadowJavaPlugin.COMPONENT_NAME] + */ public const val SHADOW: String = "shadow" public const val EXTENSION_NAME: String = SHADOW public const val CONFIGURATION_NAME: String = SHADOW - public const val COMPONENT_NAME: String = SHADOW - public const val DISTRIBUTION_NAME: String = SHADOW @get:JvmSynthetic public inline val ConfigurationContainer.shadow: NamedDomainObjectProvider 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 1f5e3bd41..23310954f 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 @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.SHADOW import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration @@ -82,7 +83,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( variant.mapToOptional() } } - val shadowComponent = softwareComponentFactory.adhoc(ShadowBasePlugin.COMPONENT_NAME) + val shadowComponent = softwareComponentFactory.adhoc(COMPONENT_NAME) components.add(shadowComponent) shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> variant.mapToMavenScope("runtime") @@ -106,6 +107,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( } public companion object { + public const val COMPONENT_NAME: String = SHADOW public const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" @get:JvmSynthetic From b71a4417947d3fbf18237892242acebbee9715f7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 17:30:43 +0800 Subject: [PATCH 608/941] Add migration example and changelog tweaks (#1591) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 56 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index cbbe26fee..99a0e2813 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -50,9 +50,9 @@ - Fail build if the ZIP entries in the shadowed JAR are duplicate. ([#1552](https://github.com/GradleUp/shadow/pull/1552)) This feature is controlled by the `shadowJar.failOnDuplicateEntries` property, which is `false` by default. Related to setting `duplicatesStrategy = DuplicatesStrategy.FAIL` but there are some differences: - - It only checks the entries in the shadowed jar, not the input files. - - It works with setting `duplicatesStrategy` to any value. - - It provides a more strict check before the JAR is created. + - It only checks the entries in the shadowed jar, not the input files. + - It works with setting `duplicatesStrategy` to any value. + - It provides a stricter fallback check before the JAR is created. ### Changed @@ -69,8 +69,12 @@ - **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) - **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) - **BREAKING CHANGE:** Change the default `duplicatesStrategy` from `EXCLUDE` to `INCLUDE`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - - `ShadowJar` recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. - - Now `ShadowJar` honors `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. + - `ShadowJar` recognized `EXCLUDE` as the default, but the other strategies didn't work properly. + - Now `ShadowJar` honors `INCLUDE` as the default, and aligns all the strategy behaviors with the Gradle side. + - `mergeServiceFiles` and `ServiceFileTransformer` do not work with `EXCLUDE`, as it will exclude extra service files to be merged. + - Duplicate entries might be bundled due to this change, but you can reduce them by using the newly added `PreserveFirstFoundResourceTransformer`. + - Use `filesMatching` to override the default strategy for specific files. + - Set `failOnDuplicateEntries = true` to fail the build to check for duplicate entries. - See more details at [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy). - **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial @@ -144,6 +148,46 @@ - **BREAKING CHANGE:** Remove `Relocator.ROLE`. ([#1563](https://github.com/GradleUp/shadow/pull/1563)) - **BREAKING CHANGE:** Remove deprecated `ShadowExtension.component`. ([#1586](https://github.com/GradleUp/shadow/pull/1586)) +### Migration Example + +**8.x** + +```kt +tasks.shadowJar { + isEnableRelocation = true + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + mergeServiceFiles() + from("foo.jar") +} +``` + +**9.x** + +```kt +tasks.shadowJar { + // `isEnableRelocation` has been renamed to `enableAutoRelocation`. + enableAutoRelocation = true + + // The default `duplicatesStrategy` has been changed from `EXCLUDE` to `INCLUDE`. + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + // If you want to make `mergeServiceFiles` work, should leave the `duplicatesStrategy` as `INCLUDE`. + // `EXCLUDE` will exclude extra service files to be merged. + mergeServiceFiles() + // If you leave `duplicatesStrategy` as `INCLUDE`, you can use the new `PreserveFirstFoundResourceTransformer` + // to ensure that only the first found resource is included in the final JAR. Or duplicate entries will be bundled. + transform() { + resources.add("META-INF/some-resource.txt") + } + // Optionally, you can enable the new `failOnDuplicateEntries` property to fail the build if there are duplicate entries. + failOnDuplicateEntries = true + + // If you want to keep the `foo.jar` as-is (zipped), you can use the `from` method directly. This is different from the previous. + from("foo.jar") + // If you want to unzip the `foo.jar` before processing, you can use `zipTree` to unzip it. + from(zipTree("foo.jar")) +} +``` + ## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 ### Added @@ -156,7 +200,7 @@ Related to setting `duplicatesStrategy = DuplicatesStrategy.FAIL` but there are some differences: - It only checks the entries in the shadowed jar, not the input files. - It works with setting `duplicatesStrategy` to any value. - - It provides a more strict check before the JAR is created. + - It provides a stricter check before the JAR is created. ### Changed From 605bb13616adecc07d9f4dd6abc29d14641d1fd7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 17:36:27 +0800 Subject: [PATCH 609/941] Remove eachFile stuff (#1592) --- docs/configuration/merging/README.md | 3 +-- .../ServiceFileTransformerTest.kt | 23 +------------------ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 5 ++-- 3 files changed, 4 insertions(+), 27 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 3c6405237..63b82dff7 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -430,7 +430,7 @@ as expected because the `duplicatesStrategy` will exclude the duplicate service Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several ways to do it: -- Use [`eachFile`][Jar.eachFile] or [`filesMatching`][Jar.filesMatching] to override the strategy for specific files. +- Use [`filesMatching`][Jar.filesMatching] to override the strategy for specific files. - Keep `duplicatesStrategy = INCLUDE` and write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates. If you just want to keep the current behavior and preserve the first found resources, there is a simple built-in one @@ -438,7 +438,6 @@ called [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTrans [AbstractCopyTask]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html -[Jar.eachFile]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:eachFile(groovy.lang.Closure) [Jar.filesMatching]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:filesMatching(java.lang.Iterable,%20org.gradle.api.Action) [AppendingTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html [DuplicatesStrategy]: https://docs.gradle.org/current/javadoc/org/gradle/api/file/DuplicatesStrategy.html diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 4b7af4ea6..d5f597c00 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -225,28 +225,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } @Test - fun strategyExcludeCanBeOverriddenByEachFile() { - writeDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) - projectScriptPath.appendText( - """ - $shadowJar { - eachFile { - duplicatesStrategy = DuplicatesStrategy.INCLUDE - } - } - """.trimIndent(), - ) - - run(shadowJarTask) - - assertThat(outputShadowJar).useAll { - getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) - getContent(ENTRY_SERVICES_FOO).isEqualTo("one\ntwo") - } - } - - @Test - fun strategyExcludeCanBeOverriddenByFileMatching() { + fun strategyExcludeCanBeOverriddenByFilesMatching() { writeDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) projectScriptPath.appendText( """ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 0dfd6dc93..386a08e9f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -196,7 +196,7 @@ public abstract class ShadowJar : Jar() { /** * Returns the strategy to use when trying to copy more than one file to the same destination. * - * This strategy can be overridden for individual files by using [eachFile] or [filesMatching]. + * This strategy can be overridden for individual files by using [filesMatching]. * * The default value is [INCLUDE]. Different strategies will lead to different results for * `foo/bar` files in the JARs to be merged: @@ -212,13 +212,12 @@ public abstract class ShadowJar : Jar() { * [EXCLUDE], as the service files are excluded beforehand. Want [ResourceTransformer]s and the strategy to work * together? There are several ways to do it: * - * - Use [eachFile] or [filesMatching] to override the strategy for specific files. + * - Use [filesMatching] to override the strategy for specific files. * - Keep `duplicatesStrategy = INCLUDE` and write your own [ResourceTransformer] to handle duplicates. * * If you just want to keep the current behavior and preserve the first found resources, there is a simple built-in one * called [PreserveFirstFoundResourceTransformer]. * - * @see [eachFile] * @see [filesMatching] * @see [DuplicatesStrategy] * @see [CopySpec.duplicatesStrategy] From 113cd7b106302553966fa321d2f89ded2df73f51 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 18:03:48 +0800 Subject: [PATCH 610/941] Enable testRetry (#1593) https://docs.gradle.com/develocity/test-distribution/#test_retry --- build.gradle.kts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index cef1872e6..f02d6ecbf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -183,6 +183,13 @@ testing.suites { testTask { systemProperty("TEST_GRADLE_VERSION", testGradleVersion) maxParallelForks = Runtime.getRuntime().availableProcessors() + + develocity { + testRetry { + maxRetries = 2 + maxFailures = 10 + } + } } } } From fc1b3cfc5a6602a5149c374db18a9cd9b698f3ea Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 18:12:53 +0800 Subject: [PATCH 611/941] Normalize functional test names (#1576) * `Dont` -> `DoNot` * Remove `can` prefixes * `does` -> `do` * Rename `addExtraFilesIntoShadowJar` to `addExtraFilesViaFrom` --- .../gradle/plugins/shadow/AndroidPluginTest.kt | 2 +- .../gradle/plugins/shadow/ApplicationPluginTest.kt | 6 +++--- .../jengelman/gradle/plugins/shadow/JavaPluginTest.kt | 10 +++++----- .../jengelman/gradle/plugins/shadow/RelocationTest.kt | 4 ++-- .../transformers/PropertiesFileTransformerTest.kt | 2 +- .../plugins/shadow/transformers/TransformersTest.kt | 10 +++++----- .../shadow/transformers/XmlAppendingTransformerTest.kt | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt index 6cce09a7b..9800c9d60 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt @@ -9,7 +9,7 @@ import org.junit.jupiter.params.provider.MethodSource class AndroidPluginTest : BasePluginTest() { @ParameterizedTest @MethodSource("androidIdsProvider") - fun doesNotCompatAgp(pluginId: String) { + fun doNotCompatAgp(pluginId: String) { projectScriptPath.writeText(getDefaultProjectBuildScript(plugin = pluginId)) assertThat(runWithFailure().output).contains( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index bfa36b438..f931db3b5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -122,7 +122,7 @@ class ApplicationPluginTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/613", ) @Test - fun canOverrideMainClassAttrInManifestBlock() { + fun overrideMainClassAttrInManifestBlock() { val main2ClassEntry = writeClass(className = "Main2") prepare( projectBlock = """ @@ -159,7 +159,7 @@ class ApplicationPluginTest : BasePluginTest() { } @Test - fun canAddExtraFilesIntoDistribution() { + fun addExtraFilesIntoDistribution() { path("extra/echo.sh").writeText("echo 'Hello, World!'") path("some/dir/hello.txt").writeText("'Hello, World!'") prepare( @@ -197,7 +197,7 @@ class ApplicationPluginTest : BasePluginTest() { } @Test - fun canIncludeSrcDistByDefault() { + fun includeSrcDistByDefault() { path("src/dist/echo.sh").writeText("echo 'Hello, World!'") prepare() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt index f39f9c4bc..ce8cd9727 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt @@ -329,7 +329,7 @@ class JavaPluginTest : BasePluginTest() { } @Test - fun doesNotIncludeCompileOnlyConfigurationByDefault() { + fun doNotIncludeCompileOnlyConfigurationByDefault() { projectScriptPath.appendText( """ dependencies { @@ -521,7 +521,7 @@ class JavaPluginTest : BasePluginTest() { ) @ParameterizedTest @ValueSource(strings = [COMPILE_ONLY_CONFIGURATION_NAME, API_CONFIGURATION_NAME]) - fun doesNotReAddSuppressedGradleApi(configuration: String) { + fun doNotReAddSuppressedGradleApi(configuration: String) { projectScriptPath.writeText( """ ${getDefaultProjectBuildScript("java-gradle-plugin")} @@ -542,7 +542,7 @@ class JavaPluginTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/1070", ) @Test - fun canRegisterCustomShadowJarTask() { + fun registerCustomShadowJarTask() { val mainClassEntry = writeClass(sourceSet = "test", withImports = true) val testShadowJarTask = "testShadowJar" projectScriptPath.appendText( @@ -691,7 +691,7 @@ class JavaPluginTest : BasePluginTest() { } @Test - fun canInheritFromOtherManifest() { + fun inheritFromOtherManifest() { projectScriptPath.appendText( """ jar { @@ -720,7 +720,7 @@ class JavaPluginTest : BasePluginTest() { } @Test - fun canAddExtraFilesIntoShadowJar() { + fun addExtraFilesViaFrom() { val mainClassEntry = writeClass() path("Foo").writeText("Foo") projectScriptPath.appendText( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 0596ae19a..79c7775d6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -325,7 +325,7 @@ class RelocationTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/294", ) @Test - fun doesNotErrorOnRelocatingJava9Classes() { + fun doNotErrorOnRelocatingJava9Classes() { projectScriptPath.appendText( """ dependencies { @@ -612,7 +612,7 @@ class RelocationTest : BasePluginTest() { ) @ParameterizedTest @ValueSource(booleans = [false, true]) - fun canDisableRelocateStringConstants(skipStringConstants: Boolean) { + fun disableRelocateStringConstants(skipStringConstants: Boolean) { writeClassWithStringRef() projectScriptPath.appendText( """ diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index a050dd1f5..800e5818e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -129,7 +129,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { "https://github.com/GradleUp/shadow/issues/856", ) @Test - fun mergedPropertiesDontContainDateComment() { + fun mergedPropertiesDoNotContainDateComment() { val one = buildJarOne { insert("META-INF/test.properties", "foo=one") } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 3e00deb79..6dd527637 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -80,7 +80,7 @@ class TransformersTest : BaseTransformerTest() { "https://github.com/GradleUp/shadow/issues/427", ) @Test - fun canMergeLog4j2PluginCacheFiles() { + fun mergeLog4j2PluginCacheFiles() { val content = requireResourceAsText(PLUGIN_CACHE_FILE) val one = buildJarOne { insert(PLUGIN_CACHE_FILE, content) @@ -107,7 +107,7 @@ class TransformersTest : BaseTransformerTest() { } @Test - fun canIncludeResource() { + fun includeResource() { val foo = path("foo").apply { writeText("foo") } projectScriptPath.appendText( transform( @@ -130,7 +130,7 @@ class TransformersTest : BaseTransformerTest() { } @Test - fun canExcludeResource() { + fun excludeResource() { val one = buildJarOne { insert("foo", "bar") insert("bar", "foo") @@ -154,7 +154,7 @@ class TransformersTest : BaseTransformerTest() { } @Test - fun canPreserveFirstFoundResource() { + fun preserveFirstFoundResource() { path("src/main/resources/foo/bar").writeText("bar1") path("src/main/resources/foo/baz").writeText("baz1") val one = buildJarOne { @@ -187,7 +187,7 @@ class TransformersTest : BaseTransformerTest() { } @Test - fun canUseCustomTransformer() { + fun useCustomTransformer() { projectScriptPath.appendText( """ dependencies { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index d3397a491..8bcdcfad7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -53,7 +53,7 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { "https://github.com/GradleUp/shadow/issues/168", ) @Test - fun canMergeNestedLevels() { + fun mergeNestedLevels() { val xmlEntry = "META-INF/nested.xml" val xmlContent = """ From d98bb7b8acf67d899834a1569a683374ac0571b6 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 6 Aug 2025 18:37:51 +0800 Subject: [PATCH 612/941] Infix for pair --- .../plugins/shadow/transformers/ManifestAppenderTransformer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index ca6f75b03..02b8c42a1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -65,7 +65,7 @@ public open class ManifestAppenderTransformer @Inject constructor( } public open fun append(name: String, value: Comparable<*>) { - attributes.add(Pair(name, value)) + attributes.add(name to value) } private companion object { From a80909d89d7bd1a981a6b372bca2a4c822ea88e7 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 6 Aug 2025 18:43:28 +0800 Subject: [PATCH 613/941] Update lint baseline --- gradle/lint-baseline.xml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index c5f355666..d0297cb3f 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -52,7 +52,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -74,7 +74,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -85,7 +85,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -151,7 +151,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -162,7 +162,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -173,7 +173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,7 +184,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> From 5dd423f175880b20d98b6e4caa2aa72df099cc43 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 6 Aug 2025 18:47:12 +0800 Subject: [PATCH 614/941] Fix typo in doc --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 99a0e2813..cda844b4d 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -169,7 +169,7 @@ tasks.shadowJar { enableAutoRelocation = true // The default `duplicatesStrategy` has been changed from `EXCLUDE` to `INCLUDE`. - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + duplicatesStrategy = DuplicatesStrategy.INCLUDE // If you want to make `mergeServiceFiles` work, should leave the `duplicatesStrategy` as `INCLUDE`. // `EXCLUDE` will exclude extra service files to be merged. mergeServiceFiles() From d946bb8f42387b39f9f445c0d39f5325e889508e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 6 Aug 2025 20:48:39 +0800 Subject: [PATCH 615/941] Small tweaks about upgrading warning (#1594) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index cda844b4d..15c392633 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -8,9 +8,11 @@ This release is a major update from the 8.x series. The plugin has been fully rewritten in Kotlin, bringing significant improvements to maintainability, performance, and future extensibility. It introduces many new features, enhancements, and bug fixes, and includes several breaking changes. Please review the changelog carefully and consult - the [new doc site](https://gradleup.com/shadow/) before upgrading. + the [new doc site](https://gradleup.com/shadow/) before upgrading. -!!! info + *If you really don't want to upgrade, you can still use the 8.3.x, which is also Gradle 9 compatible. But no additional features or crucial bug fixes will be included in the 8.x line.* + +!!! tip You can diff the shadowed JARs when upgrading from 8.x to 9.x by using [Diffuse](https://github.com/JakeWharton/diffuse). If there are any things missing in the changelog or the doc site, please report them to us. From fc9a801808b237f021a2b054c03bdd91ca583f93 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 7 Aug 2025 10:25:30 +0800 Subject: [PATCH 616/941] Document wildcard dependency matching in DependencyFilter (#1596) * Update `excludeDependencyUsingWildcardSyntax` * Add more examples for `Using Regex Patterns to Filter Dependencies` --- docs/configuration/dependencies/README.md | 9 ++++++++- .../gradle/plugins/shadow/FilteringTest.kt | 20 ++++++++++++++++--- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index d9ccc254a..c807de2df 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -229,7 +229,14 @@ Any of the individual fields can be safely absent and will function as though a The above code snippet is functionally equivalent to the previous example. -This same pattern can be used for any of the dependency notation fields. +This same pattern can be used for any of the dependency notation fields. e.g. + +- `org.apache.logging.log4j:.*` +- `org.apache.logging.log4j:log4j.*` +- `org.apache.logging.log4j:log4j-core:.*` +- `org.apache.logging.*:log4j-core:.*` +- `.*:log4j-core:.*` +- ... === "Kotlin" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 1ddd065a8..0dc2a2e31 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -8,6 +8,7 @@ import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.ValueSource class FilteringTest : BasePluginTest() { @@ -73,8 +74,9 @@ class FilteringTest : BasePluginTest() { commonAssertions() } - @Test - fun excludeDependencyUsingWildcardSyntax() { + @ParameterizedTest + @MethodSource("wildcardDepProvider") + fun excludeDependencyUsingWildcardSyntax(wildcard: String) { projectScriptPath.appendText( """ dependencies { @@ -82,7 +84,7 @@ class FilteringTest : BasePluginTest() { } $shadowJar { dependencies { - exclude(dependency('my:d:.*')) + exclude(dependency('$wildcard')) } } """.trimIndent(), @@ -240,4 +242,16 @@ class FilteringTest : BasePluginTest() { ) } } + + private companion object { + @JvmStatic + fun wildcardDepProvider() = listOf( + "my:d", + "m.*:d", + "my:d:.*", + "m.*:d:.*", + "m.*:d.*:.*", + ".*:d:.*", + ) + } } From 1c533166709877c2fd6d4ce22bb9662c1463dac5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 7 Aug 2025 11:22:05 +0800 Subject: [PATCH 617/941] Tweak update-start-scripts workflow (#1598) * Enable `cache-read-only` * Use github-actions[bot] as the author --- .github/workflows/update-start-scripts.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/update-start-scripts.yml b/.github/workflows/update-start-scripts.yml index d485c0abe..b61bc0260 100644 --- a/.github/workflows/update-start-scripts.yml +++ b/.github/workflows/update-start-scripts.yml @@ -20,6 +20,8 @@ jobs: distribution: 'zulu' java-version: 21 - uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: true - run: ./gradlew downloadStartScripts - uses: peter-evans/create-pull-request@v7 with: @@ -30,4 +32,5 @@ jobs: title: Update start script templates to the latest body: _Auto-generated by `Update Start Scripts` Github workflow._ branch: actions/update-start-scripts + author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> # Same as `committer`. delete-branch: true From 13bc68667a73ddc81b0557e2b1317c2fcb745640 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 7 Aug 2025 11:27:08 +0800 Subject: [PATCH 618/941] Prepare 9.0.0 final --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 15c392633..8a4f507b1 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [9.0.0](https://github.com/GradleUp/shadow/compare/9.0.0-rc3...HEAD) - Unreleased +## [9.0.0](https://github.com/GradleUp/shadow/compare/9.0.0) - 2025-08-07 !!! warning diff --git a/gradle.properties b/gradle.properties index f4a1af2be..b1a6c5bb9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0-SNAPSHOT +VERSION_NAME=9.0.0 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From e87362ea3b906786cdf49384b599138ae44624d2 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 7 Aug 2025 11:27:57 +0800 Subject: [PATCH 619/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 8a4f507b1..31382662e 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0...HEAD) - 2025-xx-xx + + ## [9.0.0](https://github.com/GradleUp/shadow/compare/9.0.0) - 2025-08-07 !!! warning diff --git a/gradle.properties b/gradle.properties index b1a6c5bb9..1b7458656 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.0 +VERSION_NAME=9.0.1-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From a9600c4d1b6616b2e49cf97ce2c68e024fadc047 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 7 Aug 2025 14:38:29 +0800 Subject: [PATCH 620/941] Remove JavaCompile stuff --- build.gradle.kts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f02d6ecbf..2dc158856 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,7 +42,6 @@ kotlin { languageVersion = apiVersion jvmTarget = JvmTarget.JVM_11 jvmDefault = JvmDefaultMode.NO_COMPATIBILITY - // Sync with `JavaCompile.options.release`. freeCompilerArgs.add("-Xjdk-release=11") } } @@ -224,10 +223,6 @@ kotlin.target.compilations { } } -tasks.withType().configureEach { - options.release = 11 -} - tasks.pluginUnderTestMetadata { pluginClasspath.from( testPluginClasspath, From d58214fd92a239240b19c0259cb49b0d312daf34 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 7 Aug 2025 14:53:57 +0800 Subject: [PATCH 621/941] Revert "Remove JavaCompile stuff" This reverts commit a9600c4d1b6616b2e49cf97ce2c68e024fadc047. --- build.gradle.kts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 2dc158856..f02d6ecbf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,6 +42,7 @@ kotlin { languageVersion = apiVersion jvmTarget = JvmTarget.JVM_11 jvmDefault = JvmDefaultMode.NO_COMPATIBILITY + // Sync with `JavaCompile.options.release`. freeCompilerArgs.add("-Xjdk-release=11") } } @@ -223,6 +224,10 @@ kotlin.target.compilations { } } +tasks.withType().configureEach { + options.release = 11 +} + tasks.pluginUnderTestMetadata { pluginClasspath.from( testPluginClasspath, From 7e46c168e0ef172315a0779172120dbe6e025bde Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 7 Aug 2025 16:24:38 +0800 Subject: [PATCH 622/941] Add upgrading steps for users of ServiceFileTransformer (#1600) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index 31382662e..ea3078b2c 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -193,6 +193,15 @@ tasks.shadowJar { } ``` +If you used Shadow for merging service files, the following steps are recommended: + +1. Make sure to leave `duplicatesStrategy` as default (`INCLUDE`) or `WARN`. +2. Apply `mergeServiceFiles` or `ServiceFileTransformer` stuff as you did in your previous setup. +3. Diff the JARs from upgrading or not. +4. Remove the extra entries that are added by `INCLUDE` by `filesMatching` or `PreserveFirstFoundResourceTransformer`. +5. Diff the JARs again, and check that only the entries you want to preserve remain. +6. Optionally, if you want a stricter check for the shadowed JAR entries, enable `failOnDuplicateEntries`. + ## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 ### Added From 7c352a4705c9396bd1ddecc374c6e7e86230a9f2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 7 Aug 2025 18:53:32 +0800 Subject: [PATCH 623/941] Improve the error message for empty mainClassName (#1601) * Fail build if mainClassName is empty * Update changelog * Test `errorWhenMainClassNotSet` --- docs/changes/README.md | 3 +++ .../gradle/plugins/shadow/ApplicationPluginTest.kt | 14 +++++++++++++- .../plugins/shadow/ShadowApplicationPlugin.kt | 7 +++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ea3078b2c..ee079a86f 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0...HEAD) - 2025-xx-xx +### Added + +- Improve the error message for empty `mainClassName`. ([#1601](https://github.com/GradleUp/shadow/pull/1601)) ## [9.0.0](https://github.com/GradleUp/shadow/compare/9.0.0) - 2025-08-07 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index f931db3b5..ef7d1a14c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -158,6 +158,17 @@ class ApplicationPluginTest : BasePluginTest() { assertions(run(":run").output, "bar") } + @Test + fun errorWhenMainClassNotSet() { + prepare(mainClassBlock = "") + + val result = runWithFailure(runShadowTask) + + assertThat(result.output).contains( + "The main class must be specified and not left empty in `application.mainClass` or manifest attributes.", + ) + } + @Test fun addExtraFilesIntoDistribution() { path("extra/echo.sh").writeText("echo 'Hello, World!'") @@ -220,6 +231,7 @@ class ApplicationPluginTest : BasePluginTest() { private fun prepare( mainClassWithImports: Boolean = false, projectBlock: String = "", + mainClassBlock: String = "mainClass = 'my.Main'", applicationBlock: String = "", settingsBlock: String = "", dependenciesBlock: String = "implementation 'my:a:1.0'", @@ -231,7 +243,7 @@ class ApplicationPluginTest : BasePluginTest() { apply plugin: 'application' $projectBlock application { - mainClass = 'my.Main' + $mainClassBlock $applicationBlock } dependencies { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index e6e30d304..72d362abc 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -129,11 +129,14 @@ public abstract class ShadowApplicationPlugin : Plugin { protected open fun Project.configureShadowJarMainClass() { val mainClassName = applicationExtension.mainClass tasks.shadowJar.configure { task -> - 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() + task.manifest.attributes[mainClassAttributeKey] = mainClassName.orNull.also { value -> + if (value.isNullOrEmpty()) { + error("The main class must be specified and not left empty in `application.mainClass` or manifest attributes.") + } + } } } } From 29d8c9c2f37a2aa3fa8d3f1578514f86b1a146fd Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 7 Aug 2025 20:18:35 +0800 Subject: [PATCH 624/941] Add caching tests for manifest attr changes (#1602) * Test `shadowJarIsCachedCorrectlyAfterManifestAttrsChanged` * Test `shadowJarIsCachedCorrectlyAfterKotlinMainRunChanged` * Remove `mainClassName` from `inputs` * Revert "Remove `mainClassName` from `inputs`" This reverts commit 3cd757ca9c2842d7ce7a76c7e6da5350fdd166f2. * Test `shadowJarIsCachedCorrectlyAfterApplicationChanged` * Disable it for a while * Cleanups --- .../shadow/caching/ShadowJarCachingTest.kt | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index ddb017215..f8ef875cc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -1,12 +1,16 @@ package com.github.jengelman.gradle.plugins.shadow.caching import assertk.assertThat +import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.util.containsOnly +import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText import org.gradle.api.file.DuplicatesStrategy +import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test class ShadowJarCachingTest : BaseCachingTest() { @@ -129,4 +133,108 @@ class ShadowJarCachingTest : BaseCachingTest() { assertCompositeExecutions() } } + + @Test + fun shadowJarIsCachedCorrectlyAfterManifestAttrsChanged() { + projectScriptPath.appendText( + """ + jar { + manifest { + attributes 'Foo': 'Foo1' + } + } + $shadowJar { + manifest { + attributes 'Bar': 'Bar1' + } + } + """.trimIndent(), + ) + + val assertions = { valueFoo: String, valueBar: String -> + assertCompositeExecutions { + getMainAttr("Foo").isEqualTo(valueFoo) + getMainAttr("Bar").isEqualTo(valueBar) + } + } + + assertions("Foo1", "Bar1") + + var replaced = projectScriptPath.readText().replace("Foo1", "Foo2") + projectScriptPath.writeText(replaced) + + assertions("Foo2", "Bar1") + + replaced = projectScriptPath.readText().replace("Bar1", "Bar2") + projectScriptPath.writeText(replaced) + + assertions("Foo2", "Bar2") + + replaced = projectScriptPath.readText() + .replace("Foo2", "Foo3") + .replace("Bar2", "Bar3") + projectScriptPath.writeText(replaced) + + assertions("Foo3", "Bar3") + } + + @Test + fun shadowJarIsCachedCorrectlyAfterKotlinMainRunChanged() { + val mainClassName = "my.Main" + val main2ClassName = "my.Main2" + + val projectBuildScript = getDefaultProjectBuildScript( + plugin = "org.jetbrains.kotlin.multiplatform", + withGroup = true, + withVersion = true, + ) + projectScriptPath.writeText( + """ + $projectBuildScript + kotlin { + jvm().mainRun { + it.mainClass.set('$mainClassName') + } + } + """.trimIndent(), + ) + + assertCompositeExecutions { + getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) + } + + val replaced = projectScriptPath.readText().replace(mainClassName, main2ClassName) + projectScriptPath.writeText(replaced) + + assertCompositeExecutions { + getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) + } + } + + @Disabled("TODO: https://github.com/GradleUp/shadow/pull/1601#discussion_r2260096815") + @Test + fun shadowJarIsCachedCorrectlyAfterApplicationChanged() { + val mainClassName = "my.Main" + val main2ClassName = "my.Main2" + + projectScriptPath.appendText( + """ + apply plugin: 'application' + application { + mainClass = '$mainClassName' + } + """.trimIndent(), + ) + + assertCompositeExecutions { + getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) + } + + val replaced = projectScriptPath.readText().replace(mainClassName, main2ClassName) + projectScriptPath.writeText(replaced) + + assertCompositeExecutions { + getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) + } + } } From f4a2fb22253be2696d72cd51aff4a5167d09474b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 7 Aug 2025 20:59:44 +0800 Subject: [PATCH 625/941] Fix the regression of the mainClassName input (#1603) * Enable `shadowJarIsCachedCorrectlyAfterApplicationChanged` * Add `mainClassName` input back * Wrap `applicationExtension.mainClass` as a provider * Revert "Wrap `applicationExtension.mainClass` as a provider" This reverts commit e82de5acf1d96a5287efeebfb5d8590f3f81bd9c. * Use `convention` --- .../plugins/shadow/caching/ShadowJarCachingTest.kt | 2 -- .../gradle/plugins/shadow/ShadowApplicationPlugin.kt | 12 +++++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index f8ef875cc..cc72bf4aa 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -10,7 +10,6 @@ import kotlin.io.path.appendText import kotlin.io.path.readText import kotlin.io.path.writeText import org.gradle.api.file.DuplicatesStrategy -import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test class ShadowJarCachingTest : BaseCachingTest() { @@ -211,7 +210,6 @@ class ShadowJarCachingTest : BaseCachingTest() { } } - @Disabled("TODO: https://github.com/GradleUp/shadow/pull/1601#discussion_r2260096815") @Test fun shadowJarIsCachedCorrectlyAfterApplicationChanged() { val mainClassName = "my.Main" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 72d362abc..a6d6743d6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -127,16 +127,18 @@ public abstract class ShadowApplicationPlugin : Plugin { } protected open fun Project.configureShadowJarMainClass() { - val mainClassName = applicationExtension.mainClass + // Default to empty string to avoid the error of the value not being configured yet. + val mainClassName = applicationExtension.mainClass.convention("") tasks.shadowJar.configure { task -> + 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.orNull.also { value -> - if (value.isNullOrEmpty()) { - error("The main class must be specified and not left empty in `application.mainClass` or manifest attributes.") - } + val realClass = mainClassName.orNull + if (realClass.isNullOrEmpty()) { + error("The main class must be specified and not left empty in `application.mainClass` or manifest attributes.") } + task.manifest.attributes[mainClassAttributeKey] = realClass } } } From 8cb83b2ee4d4f3a63cc27075f77c0ec48a24a754 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 7 Aug 2025 22:43:21 +0800 Subject: [PATCH 626/941] Drop redundant caching tests and normalize names (#1605) --- .../shadow/caching/ApplicationCachingTest.kt | 83 ---------- .../plugins/shadow/caching/BaseCachingTest.kt | 2 +- .../shadow/caching/FilteringCachingTest.kt | 83 +--------- .../shadow/caching/MinimizationCachingTest.kt | 5 +- .../shadow/caching/RelocationCachingTest.kt | 2 +- .../shadow/caching/ShadowJarCachingTest.kt | 14 +- .../shadow/caching/TransformerCachingTest.kt | 143 +----------------- 7 files changed, 18 insertions(+), 314 deletions(-) delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt deleted file mode 100644 index 9f17afede..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ApplicationCachingTest.kt +++ /dev/null @@ -1,83 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import kotlin.io.path.appendText -import kotlin.io.path.writeText -import org.junit.jupiter.api.Test - -class ApplicationCachingTest : BaseCachingTest() { - override val taskPath: String = runShadowTask - - @Test - fun runShadowExecutedCorrectlyAfterShadowJarChanged() { - prepare() - val resourcePath = path("src/main/resources/my/resource.txt") - resourcePath.writeText( - """ - Hello, World! %s from resource 1 - """.trimIndent(), - ) - val assertions = { resName: String -> - assertExecutionSuccess("Hello, World! foo from $resName") - // `runTask` is not cacheable, so it's always executed. - assertExecutionSuccess("Hello, World! foo from $resName") - } - - path("src/main/java/my/App.java").writeText( - """ - package my; - public class App { - public static void main(String[] args) throws Exception { - if (args.length == 0) { - throw new IllegalArgumentException("No arguments provided."); - } - var is = App.class.getResourceAsStream("resource.txt"); - String content = String.format(new String(is.readAllBytes()), (Object[]) args); - System.out.println(content); - } - } - """.trimIndent(), - ) - - assertions("resource 1") - - resourcePath.writeText( - """ - Hello, World! %s from resource 2 - """.trimIndent(), - ) - - assertions("resource 2") - } - - private fun prepare( - projectBlock: String = "", - applicationBlock: String = "", - settingsBlock: String = "", - dependenciesBlock: String = "", - runShadowBlock: String = "", - ) { - projectScriptPath.appendText( - """ - apply plugin: 'application' - $projectBlock - application { - mainClass = 'my.App' - $applicationBlock - } - dependencies { - $dependenciesBlock - } - $runShadow { - args 'foo' - $runShadowBlock - } - """.trimIndent(), - ) - settingsScriptPath.writeText( - getDefaultSettingsBuildScript( - startBlock = settingsBlock, - endBlock = "rootProject.name = 'myapp'", - ), - ) - } -} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt index 5a527a21f..05c32980d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt @@ -19,7 +19,7 @@ import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE abstract class BaseCachingTest : BasePluginTest() { - open val taskPath: String = shadowJarTask + open var taskPath: String = shadowJarTask fun cleanOutputs() { run("clean") diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt index 948e8c375..d6d8a4a75 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt @@ -1,71 +1,17 @@ package com.github.jengelman.gradle.plugins.shadow.caching -import assertk.Assert import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText -import kotlin.io.path.readText -import kotlin.io.path.writeText -import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.Test class FilteringCachingTest : BaseCachingTest() { - @BeforeAll - override fun doFirst() { - super.doFirst() - publishArtifactCD() - } - - @Test - fun dependencyExclusionsAffectUpToDateCheck() { - dependOnAndExcludeArtifactD() - - assertCompositeExecutions { - commonAssertions() - } - - val replaced = projectScriptPath.readText() - .replace("exclude(dependency('my:d:1.0'))", "exclude(dependency('my:c:1.0'))") - projectScriptPath.writeText(replaced) - - assertCompositeExecutions { - containsOnly( - "d.properties", - *entriesInAB, - *manifestEntries, - ) - } - } - - @Test - fun projectExclusionsAffectUpToDateCheck() { - dependOnAndExcludeArtifactD() - - assertCompositeExecutions { - commonAssertions() - } - - val replaced = projectScriptPath.readText() - .replace("exclude(dependency('my:d:1.0'))", "exclude 'a.properties'") - projectScriptPath.writeText(replaced) - - assertCompositeExecutions { - containsOnly( - "a2.properties", - "b.properties", - "c.properties", - "d.properties", - *manifestEntries, - ) - } - } @Issue( "https://github.com/GradleUp/shadow/issues/717", ) @Test - fun shadowJarIsCachedCorrectlyWhenUsingIncludesExcludes() { + fun jarIncludesExcludesChanged() { val mainClassEntry = writeClass(className = "Main") val main2ClassEntry = writeClass(className = "Main2") projectScriptPath.appendText( @@ -139,7 +85,7 @@ class FilteringCachingTest : BaseCachingTest() { } @Test - fun shadowJarIsCachedCorrectlyWhenUsingDependencyIncludesExcludes() { + fun dependenciesIncludesExcludesChanged() { val mainClassEntry = writeClass(withImports = true) projectScriptPath.appendText( """ @@ -176,29 +122,4 @@ class FilteringCachingTest : BaseCachingTest() { ) } } - - private fun dependOnAndExcludeArtifactD() { - projectScriptPath.appendText( - """ - dependencies { - implementation 'my:a:1.0' - implementation 'my:b:1.0' - implementation 'my:d:1.0' - } - $shadowJar { - dependencies { - exclude(dependency('my:d:1.0')) - } - } - """.trimIndent(), - ) - } - - private fun Assert.commonAssertions() { - containsOnly( - "c.properties", - *entriesInAB, - *manifestEntries, - ) - } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt index d7e82b049..08ac4acb7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt @@ -7,11 +7,12 @@ import kotlin.io.path.writeText import org.junit.jupiter.api.Test class MinimizationCachingTest : BaseCachingTest() { - override val taskPath: String = serverShadowJarTask override val outputShadowJar: JarPath get() = outputServerShadowJar @Test - fun shadowJarIsCachedCorrectlyWhenMinimizationIsAdded() { + fun minimizeChanged() { + taskPath = serverShadowJarTask + writeClientAndServerModules() path("server/src/main/java/server/Server.java").writeText( """ diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt index 38a9ee786..8a726f5a5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt @@ -9,7 +9,7 @@ class RelocationCachingTest : BaseCachingTest() { * Ensure that we get a cache miss when relocation changes and that caching works with relocation */ @Test - fun shadowJarIsCachedCorrectlyWhenRelocationIsAdded() { + fun relocatorAdded() { projectScriptPath.appendText( """ dependencies { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt index cc72bf4aa..435ab5393 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt @@ -17,7 +17,7 @@ class ShadowJarCachingTest : BaseCachingTest() { * Ensure that a basic usage reuses an output from cache and then gets a cache miss when the content changes. */ @Test - fun shadowJarIsCachedCorrectlyWhenCopying() { + fun dependenciesChanged() { projectScriptPath.appendText( """ dependencies { @@ -47,7 +47,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } @Test - fun shadowJarIsCachedCorrectlyWhenOutputFileIsChanged() { + fun outputFileChanged() { projectScriptPath.appendText( """ dependencies { @@ -81,7 +81,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } @Test - fun shadowJarIsCachedCorrectlyAfterDependencyFilterChanged() { + fun dependencyFilterChanged() { publishArtifactCD() projectScriptPath.appendText( """ @@ -114,7 +114,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } @Test - fun shadowJarIsCachedCorrectlyAfterDuplicatesStrategyChanged() { + fun duplicatesStrategyChanged() { listOf( DuplicatesStrategy.EXCLUDE, DuplicatesStrategy.INCLUDE, @@ -134,7 +134,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } @Test - fun shadowJarIsCachedCorrectlyAfterManifestAttrsChanged() { + fun manifestAttrsChanged() { projectScriptPath.appendText( """ jar { @@ -178,7 +178,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } @Test - fun shadowJarIsCachedCorrectlyAfterKotlinMainRunChanged() { + fun kotlinMainRunChanged() { val mainClassName = "my.Main" val main2ClassName = "my.Main2" @@ -211,7 +211,7 @@ class ShadowJarCachingTest : BaseCachingTest() { } @Test - fun shadowJarIsCachedCorrectlyAfterApplicationChanged() { + fun applicationChanged() { val mainClassName = "my.Main" val main2ClassName = "my.Main2" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt index 2b317ce75..91a20044d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt @@ -1,48 +1,22 @@ package com.github.jengelman.gradle.plugins.shadow.caching -import assertk.assertions.contains -import assertk.assertions.isEqualTo -import com.github.jengelman.gradle.plugins.shadow.transformers.ApacheLicenseResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ApacheNoticeResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ComponentsXmlResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestAppenderTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ManifestResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText -import kotlin.io.path.deleteExisting import kotlin.io.path.readText import kotlin.io.path.writeText -import kotlin.reflect.KClass -import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource class TransformerCachingTest : BaseCachingTest() { - private lateinit var mainClass: String - - @BeforeEach - override fun setup() { - super.setup() - mainClass = writeClass() - } - @Test - fun shadowJarIsCachedCorrectlyWhenUsingServiceFileTransformer() { + fun serviceFileTransformerPropsChanged() { + val mainClassEntry = writeClass() val assertions = { assertCompositeExecutions { containsOnly( "my/", - mainClass, + mainClassEntry, *manifestEntries, ) } @@ -67,79 +41,7 @@ class TransformerCachingTest : BaseCachingTest() { } @Test - fun shadowJarIsCachedCorrectlyWhenUsingAppendingTransformer() { - path("src/main/resources/foo/bar.properties").writeText("foo=bar") - val assertions = { name: String -> - assertCompositeExecutions { - containsOnly( - "my/", - "foo/", - "foo/$name.properties", - mainClass, - *manifestEntries, - ) - getContent("foo/$name.properties").isEqualTo("foo=$name") - } - } - - assertions("bar") - - projectScriptPath.appendText( - transform( - transformerBlock = """ - resource = 'foo/bar.properties' - """.trimIndent(), - ), - ) - - assertions("bar") - - path("src/main/resources/foo/bar.properties").deleteExisting() - path("src/main/resources/foo/baz.properties").writeText("foo=baz") - val replaced = projectScriptPath.readText().replace("foo/bar.properties", "foo/baz.properties") - projectScriptPath.writeText(replaced) - - assertions("baz") - } - - @Test - fun shadowJarIsCachedCorrectlyWhenUsingXmlAppendingTransformer() { - path("src/main/resources/foo/bar.xml").writeText("bar") - val assertions = { name: String -> - assertCompositeExecutions { - containsOnly( - "my/", - "foo/", - "foo/$name.xml", - mainClass, - *manifestEntries, - ) - getContent("foo/$name.xml").contains("$name") - } - } - - assertions("bar") - - projectScriptPath.appendText( - transform( - transformerBlock = """ - resource = 'foo/bar.xml' - """.trimIndent(), - ), - ) - - assertions("bar") - - path("src/main/resources/foo/bar.xml").deleteExisting() - path("src/main/resources/foo/baz.xml").writeText("baz") - val replaced = projectScriptPath.readText().replace("foo/bar.xml", "foo/baz.xml") - projectScriptPath.writeText(replaced) - - assertions("baz") - } - - @Test - fun shadowJarCacheDisabledIfAnyTransformerIsNotCacheable() { + fun disableCacheIfAnyTransformerIsNotCacheable() { projectScriptPath.appendText( """ $shadowJar { @@ -174,41 +76,4 @@ class TransformerCachingTest : BaseCachingTest() { // The shadowJar task should be executed again as the cache is disabled. assertExecutionSuccess() } - - @ParameterizedTest - @MethodSource("transformerConfigProvider") - fun shadowJarIsCachedCorrectlyWhenUsingOtherTransformers(pair: Pair>) { - val (configuration, transformer) = pair - val assertions = { - assertCompositeExecutions { - containsAtLeast(mainClass) - } - } - - assertions() - - projectScriptPath.appendText( - """ - $shadowJar { - transform(${transformer.java.name}) $configuration - } - """.trimIndent(), - ) - - assertions() - } - - private companion object { - @JvmStatic - fun transformerConfigProvider() = listOf( - "" to ApacheLicenseResourceTransformer::class, - "" to ApacheNoticeResourceTransformer::class, - "" to ComponentsXmlResourceTransformer::class, - "" to GroovyExtensionModuleTransformer::class, - "" to Log4j2PluginsCacheFileTransformer::class, - "" to ManifestAppenderTransformer::class, - "" to ManifestResourceTransformer::class, - "{ keyTransformer = { it.toLowerCase() } }" to PropertiesFileTransformer::class, - ) - } } From 64f519399fa542e35c090e5e921bbdb417e05d46 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 7 Aug 2025 23:35:29 +0800 Subject: [PATCH 627/941] Group all caching tests into a new class (#1607) --- .../gradle/plugins/shadow/CachingTest.kt | 573 ++++++++++++++++++ .../plugins/shadow/caching/BaseCachingTest.kt | 70 --- .../shadow/caching/FilteringCachingTest.kt | 125 ---- .../shadow/caching/MinimizationCachingTest.kt | 54 -- .../shadow/caching/RelocationCachingTest.kt | 52 -- .../shadow/caching/ShadowJarCachingTest.kt | 238 -------- .../shadow/caching/TransformerCachingTest.kt | 79 --- 7 files changed, 573 insertions(+), 618 deletions(-) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt new file mode 100644 index 000000000..5742e161c --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -0,0 +1,573 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.Assert +import assertk.assertFailure +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.isEmpty +import assertk.assertions.isEqualTo +import assertk.assertions.isInstanceOf +import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer +import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.JarPath +import com.github.jengelman.gradle.plugins.shadow.util.containsOnly +import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr +import kotlin.io.path.ExperimentalPathApi +import kotlin.io.path.appendText +import kotlin.io.path.isDirectory +import kotlin.io.path.name +import kotlin.io.path.readText +import kotlin.io.path.walk +import kotlin.io.path.writeText +import org.gradle.api.file.DuplicatesStrategy +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.Test + +class CachingTest : BasePluginTest() { + private var taskPath: String = shadowJarTask + + /** + * Ensure that a basic usage reuses an output from cache and then gets a cache miss when the content changes. + */ + @Test + fun dependenciesChanged() { + projectScriptPath.appendText( + """ + dependencies { + ${implementationFiles(artifactAJar, artifactBJar)} + } + """.trimIndent(), + ) + + assertCompositeExecutions { + containsOnly( + *entriesInAB, + *manifestEntries, + ) + } + + val replaced = projectScriptPath.readText().lines() + .filterNot { it == implementationFiles(artifactBJar) } + .joinToString(lineSeparator) + projectScriptPath.writeText(replaced) + + assertCompositeExecutions { + containsOnly( + *entriesInA, + *manifestEntries, + ) + } + } + + @Test + fun outputFileChanged() { + projectScriptPath.appendText( + """ + dependencies { + ${implementationFiles(artifactAJar, artifactBJar)} + } + """.trimIndent() + lineSeparator, + ) + + assertCompositeExecutions { + containsOnly( + *entriesInAB, + *manifestEntries, + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + archiveBaseName = "foo" + } + """.trimIndent(), + ) + + assertExecutionsFromCacheAndUpToDate() + assertThat(jarPath("build/libs/foo-1.0-all.jar")).useAll { + containsOnly( + *entriesInAB, + *manifestEntries, + ) + } + } + + @Test + fun dependencyFilterChanged() { + publishArtifactCD() + projectScriptPath.appendText( + """ + dependencies { + implementation 'my:d:1.0' + } + """.trimIndent() + lineSeparator, + ) + val assertions = { + assertCompositeExecutions { + containsOnly( + "c.properties", + "d.properties", + *manifestEntries, + ) + } + } + + assertions() + + projectScriptPath.appendText( + """ + $shadowJar { + dependencyFilter = new ${MinimizeDependencyFilter::class.java.name}(project) + } + """.trimIndent(), + ) + + assertions() + } + + @Test + fun duplicatesStrategyChanged() { + listOf( + DuplicatesStrategy.EXCLUDE, + DuplicatesStrategy.INCLUDE, + DuplicatesStrategy.WARN, + ).forEach { strategy -> + projectScriptPath.writeText( + getDefaultProjectBuildScript(withGroup = true, withVersion = true) + + """ + $shadowJar { + duplicatesStrategy = DuplicatesStrategy.$strategy + } + """.trimIndent(), + ) + + assertCompositeExecutions() + } + } + + @Test + fun manifestAttrsChanged() { + projectScriptPath.appendText( + """ + jar { + manifest { + attributes 'Foo': 'Foo1' + } + } + $shadowJar { + manifest { + attributes 'Bar': 'Bar1' + } + } + """.trimIndent(), + ) + + val assertions = { valueFoo: String, valueBar: String -> + assertCompositeExecutions { + getMainAttr("Foo").isEqualTo(valueFoo) + getMainAttr("Bar").isEqualTo(valueBar) + } + } + + assertions("Foo1", "Bar1") + + var replaced = projectScriptPath.readText().replace("Foo1", "Foo2") + projectScriptPath.writeText(replaced) + + assertions("Foo2", "Bar1") + + replaced = projectScriptPath.readText().replace("Bar1", "Bar2") + projectScriptPath.writeText(replaced) + + assertions("Foo2", "Bar2") + + replaced = projectScriptPath.readText() + .replace("Foo2", "Foo3") + .replace("Bar2", "Bar3") + projectScriptPath.writeText(replaced) + + assertions("Foo3", "Bar3") + } + + @Test + fun kotlinMainRunChanged() { + val mainClassName = "my.Main" + val main2ClassName = "my.Main2" + + val projectBuildScript = getDefaultProjectBuildScript( + plugin = "org.jetbrains.kotlin.multiplatform", + withGroup = true, + withVersion = true, + ) + projectScriptPath.writeText( + """ + $projectBuildScript + kotlin { + jvm().mainRun { + it.mainClass.set('$mainClassName') + } + } + """.trimIndent(), + ) + + assertCompositeExecutions { + getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) + } + + val replaced = projectScriptPath.readText().replace(mainClassName, main2ClassName) + projectScriptPath.writeText(replaced) + + assertCompositeExecutions { + getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) + } + } + + @Test + fun applicationChanged() { + val mainClassName = "my.Main" + val main2ClassName = "my.Main2" + + projectScriptPath.appendText( + """ + apply plugin: 'application' + application { + mainClass = '$mainClassName' + } + """.trimIndent(), + ) + + assertCompositeExecutions { + getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) + } + + val replaced = projectScriptPath.readText().replace(mainClassName, main2ClassName) + projectScriptPath.writeText(replaced) + + assertCompositeExecutions { + getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) + } + } + + @Issue( + "https://github.com/GradleUp/shadow/issues/717", + ) + @Test + fun jarIncludesExcludesChanged() { + val mainClassEntry = writeClass(className = "Main") + val main2ClassEntry = writeClass(className = "Main2") + projectScriptPath.appendText( + """ + dependencies { + implementation 'my:a:1.0' + implementation 'my:b:1.0' + } + """.trimIndent() + lineSeparator, + ) + + assertCompositeExecutions { + containsOnly( + "my/", + mainClassEntry, + main2ClassEntry, + *entriesInAB, + *manifestEntries, + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + exclude '**.properties' + } + """.trimIndent() + lineSeparator, + ) + + assertCompositeExecutions { + containsOnly( + "my/", + mainClassEntry, + main2ClassEntry, + *manifestEntries, + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + include '$mainClassEntry' + } + """.trimIndent() + lineSeparator, + ) + + assertCompositeExecutions { + containsOnly( + "my/", + mainClassEntry, + *manifestEntries, + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + include '$main2ClassEntry' + } + """.trimIndent() + lineSeparator, + ) + + assertCompositeExecutions { + containsOnly( + "my/", + mainClassEntry, + main2ClassEntry, + *manifestEntries, + ) + } + } + + @Test + fun dependenciesIncludesExcludesChanged() { + val mainClassEntry = writeClass(withImports = true) + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent() + lineSeparator, + ) + + assertCompositeExecutions { + containsOnly( + "my/", + mainClassEntry, + *junitEntries, + *manifestEntries, + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + dependencies { + exclude(dependency('junit:junit')) + } + } + """.trimIndent(), + ) + + assertCompositeExecutions { + containsOnly( + "my/", + mainClassEntry, + *manifestEntries, + ) + } + } + + @Test + fun minimizeChanged() { + taskPath = serverShadowJarTask + + writeClientAndServerModules() + path("server/src/main/java/server/Server.java").writeText( + """ + package server; + public class Server {} + """.trimIndent(), + ) + + assertCompositeExecutions( + jarPathProvider = { outputServerShadowJar }, + ) { + containsOnly( + "client/", + "server/", + "client/Client.class", + "server/Server.class", + *junitEntries, + *manifestEntries, + ) + } + + path("server/build.gradle").appendText( + """ + $shadowJar { + minimize { + exclude(dependency('junit:junit:.*')) + } + } + """.trimIndent(), + ) + + assertCompositeExecutions( + jarPathProvider = { outputServerShadowJar }, + ) { + containsOnly( + "server/", + "server/Server.class", + *junitEntries, + *manifestEntries, + ) + } + } + + /** + * Ensure that we get a cache miss when relocation changes and that caching works with relocation + */ + @Test + fun relocatorAdded() { + projectScriptPath.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent() + lineSeparator, + ) + val mainClassEntry = writeClass(withImports = true) + + assertCompositeExecutions { + containsOnly( + "my/", + mainClassEntry, + *junitEntries, + *manifestEntries, + ) + } + + projectScriptPath.appendText( + """ + $shadowJar { + relocate 'junit.framework', 'foo.junit.framework' + } + """.trimIndent(), + ) + val relocatedEntries = junitEntries + .map { it.replace("junit/framework/", "foo/junit/framework/") }.toTypedArray() + + assertCompositeExecutions { + containsOnly( + "my/", + "foo/", + "foo/junit/", + mainClassEntry, + *relocatedEntries, + *manifestEntries, + ) + } + } + + @Test + fun serviceFileTransformerPropsChanged() { + val mainClassEntry = writeClass() + val assertions = { + assertCompositeExecutions { + containsOnly( + "my/", + mainClassEntry, + *manifestEntries, + ) + } + } + + assertions() + + projectScriptPath.appendText( + transform( + transformerBlock = """ + path = 'META-INF/foo' + """.trimIndent(), + ), + ) + + assertions() + + val replaced = projectScriptPath.readText().replace("META-INF/foo", "META-INF/bar") + projectScriptPath.writeText(replaced) + + assertions() + } + + @Test + fun disableCacheIfAnyTransformerIsNotCacheable() { + projectScriptPath.appendText( + """ + $shadowJar { + mergeServiceFiles() + } + """.trimIndent() + lineSeparator, + ) + + assertCompositeExecutions() + + projectScriptPath.appendText( + """ + $shadowJar { + mergeGroovyExtensionModules() + } + """.trimIndent() + lineSeparator, + ) + + assertCompositeExecutions() + + projectScriptPath.appendText( + """ + $shadowJar { + // Use Transformer.Companion (no-op) to mock a custom transformer here, it's not cacheable. + transform(${ResourceTransformer.Companion::class.java.name}) + } + """.trimIndent(), + ) + + assertExecutionSuccess() + cleanOutputs() + // The shadowJar task should be executed again as the cache is disabled. + assertExecutionSuccess() + } + + private fun cleanOutputs() { + run("clean") + @OptIn(ExperimentalPathApi::class) + val buildDirs = projectRoot.walk().filter { it.isDirectory() && it.name == "build" } + // Make sure build folders are deleted by clean task. + assertThat(buildDirs).isEmpty() + } + + private fun assertExecutionSuccess() { + // The task was executed and not pulled from cache. + assertRunWithResult(TaskOutcome.SUCCESS) + } + + /** + * This should be called after [assertExecutionSuccess] to ensure that the [taskPath] is cached. + */ + private fun assertExecutionsFromCacheAndUpToDate() { + cleanOutputs() + // Run the task again to ensure it is pulled from cache. + assertRunWithResult(TaskOutcome.FROM_CACHE) + // Run the task again to ensure it is up-to-date. + assertRunWithResult(TaskOutcome.UP_TO_DATE) + } + + /** + * Combines [assertExecutionSuccess] and [assertExecutionsFromCacheAndUpToDate] for simplifying assertions. + */ + private fun assertCompositeExecutions( + jarPathProvider: () -> JarPath = { outputShadowJar }, + jarPathAssertions: Assert.() -> Unit = {}, + ) { + // First run should execute. + assertExecutionSuccess() + assertThat(jarPathProvider()).useAll(jarPathAssertions) + // Subsequent runs should be from cache and up-to-date after configurations changed. + assertExecutionsFromCacheAndUpToDate() + } + + private fun assertRunWithResult(expectedOutcome: TaskOutcome) { + val result = run(taskPath) + assertThat(result).taskOutcomeEquals(taskPath, expectedOutcome) + } +} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt deleted file mode 100644 index 05c32980d..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/BaseCachingTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import assertk.Assert -import assertk.assertFailure -import assertk.assertThat -import assertk.assertions.contains -import assertk.assertions.isEmpty -import assertk.assertions.isInstanceOf -import com.github.jengelman.gradle.plugins.shadow.BasePluginTest -import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import java.nio.file.NoSuchFileException -import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.isDirectory -import kotlin.io.path.name -import kotlin.io.path.walk -import org.gradle.testkit.runner.TaskOutcome -import org.gradle.testkit.runner.TaskOutcome.FROM_CACHE -import org.gradle.testkit.runner.TaskOutcome.SUCCESS -import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE - -abstract class BaseCachingTest : BasePluginTest() { - open var taskPath: String = shadowJarTask - - fun cleanOutputs() { - run("clean") - // Make sure the output shadow jar has been deleted. - assertFailure { outputShadowJar.close() }.isInstanceOf(NoSuchFileException::class) - @OptIn(ExperimentalPathApi::class) - val buildDirs = projectRoot.walk().filter { it.isDirectory() && it.name == "build" } - // Make sure build folders are deleted by clean task. - assertThat(buildDirs).isEmpty() - } - - fun assertExecutionSuccess(vararg outputs: String) { - // The task was executed and not pulled from cache. - assertRunWithResult(SUCCESS, outputs = outputs) - } - - /** - * This should be called after [assertExecutionSuccess] to ensure that the [taskPath] is cached. - */ - fun assertExecutionsFromCacheAndUpToDate(vararg outputs: String) { - cleanOutputs() - // Run the task again to ensure it is pulled from cache. - assertRunWithResult(FROM_CACHE, outputs = outputs) - // Run the task again to ensure it is up-to-date. - assertRunWithResult(UP_TO_DATE, outputs = outputs) - } - - /** - * Combines [assertExecutionSuccess] and [assertExecutionsFromCacheAndUpToDate] for simplifying assertions. - */ - fun assertCompositeExecutions( - vararg outputs: String, - jarPathProvider: () -> JarPath = { outputShadowJar }, - jarPathAssertions: Assert.() -> Unit = {}, - ) { - // First run should execute. - assertExecutionSuccess() - assertThat(jarPathProvider()).useAll(jarPathAssertions) - // Subsequent runs should be from cache and up-to-date after configurations changed. - assertExecutionsFromCacheAndUpToDate(outputs = outputs) - } - - private fun assertRunWithResult(expectedOutcome: TaskOutcome, vararg outputs: String) { - val result = run(taskPath) - assertThat(result).taskOutcomeEquals(taskPath, expectedOutcome) - assertThat(result.output).contains(*outputs) - } -} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt deleted file mode 100644 index d6d8a4a75..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/FilteringCachingTest.kt +++ /dev/null @@ -1,125 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import kotlin.io.path.appendText -import org.junit.jupiter.api.Test - -class FilteringCachingTest : BaseCachingTest() { - - @Issue( - "https://github.com/GradleUp/shadow/issues/717", - ) - @Test - fun jarIncludesExcludesChanged() { - val mainClassEntry = writeClass(className = "Main") - val main2ClassEntry = writeClass(className = "Main2") - projectScriptPath.appendText( - """ - dependencies { - implementation 'my:a:1.0' - implementation 'my:b:1.0' - } - """.trimIndent() + lineSeparator, - ) - - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - main2ClassEntry, - *entriesInAB, - *manifestEntries, - ) - } - - projectScriptPath.appendText( - """ - $shadowJar { - exclude '**.properties' - } - """.trimIndent() + lineSeparator, - ) - - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - main2ClassEntry, - *manifestEntries, - ) - } - - projectScriptPath.appendText( - """ - $shadowJar { - include '$mainClassEntry' - } - """.trimIndent() + lineSeparator, - ) - - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *manifestEntries, - ) - } - - projectScriptPath.appendText( - """ - $shadowJar { - include '$main2ClassEntry' - } - """.trimIndent() + lineSeparator, - ) - - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - main2ClassEntry, - *manifestEntries, - ) - } - } - - @Test - fun dependenciesIncludesExcludesChanged() { - val mainClassEntry = writeClass(withImports = true) - projectScriptPath.appendText( - """ - dependencies { - implementation 'junit:junit:3.8.2' - } - """.trimIndent() + lineSeparator, - ) - - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) - } - - projectScriptPath.appendText( - """ - $shadowJar { - dependencies { - exclude(dependency('junit:junit')) - } - } - """.trimIndent(), - ) - - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *manifestEntries, - ) - } - } -} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt deleted file mode 100644 index 08ac4acb7..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/MinimizationCachingTest.kt +++ /dev/null @@ -1,54 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import kotlin.io.path.appendText -import kotlin.io.path.writeText -import org.junit.jupiter.api.Test - -class MinimizationCachingTest : BaseCachingTest() { - override val outputShadowJar: JarPath get() = outputServerShadowJar - - @Test - fun minimizeChanged() { - taskPath = serverShadowJarTask - - writeClientAndServerModules() - path("server/src/main/java/server/Server.java").writeText( - """ - package server; - public class Server {} - """.trimIndent(), - ) - - assertCompositeExecutions { - containsOnly( - "client/", - "server/", - "client/Client.class", - "server/Server.class", - *junitEntries, - *manifestEntries, - ) - } - - path("server/build.gradle").appendText( - """ - $shadowJar { - minimize { - exclude(dependency('junit:junit:.*')) - } - } - """.trimIndent(), - ) - - assertCompositeExecutions { - containsOnly( - "server/", - "server/Server.class", - *junitEntries, - *manifestEntries, - ) - } - } -} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt deleted file mode 100644 index 8a726f5a5..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/RelocationCachingTest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import kotlin.io.path.appendText -import org.junit.jupiter.api.Test - -class RelocationCachingTest : BaseCachingTest() { - /** - * Ensure that we get a cache miss when relocation changes and that caching works with relocation - */ - @Test - fun relocatorAdded() { - projectScriptPath.appendText( - """ - dependencies { - implementation 'junit:junit:3.8.2' - } - """.trimIndent() + lineSeparator, - ) - val mainClassEntry = writeClass(withImports = true) - - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) - } - - projectScriptPath.appendText( - """ - $shadowJar { - relocate 'junit.framework', 'foo.junit.framework' - } - """.trimIndent(), - ) - val relocatedEntries = junitEntries - .map { it.replace("junit/framework/", "foo/junit/framework/") }.toTypedArray() - - assertCompositeExecutions { - containsOnly( - "my/", - "foo/", - "foo/junit/", - mainClassEntry, - *relocatedEntries, - *manifestEntries, - ) - } - } -} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt deleted file mode 100644 index 435ab5393..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/ShadowJarCachingTest.kt +++ /dev/null @@ -1,238 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import assertk.assertThat -import assertk.assertions.isEqualTo -import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter -import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr -import kotlin.io.path.appendText -import kotlin.io.path.readText -import kotlin.io.path.writeText -import org.gradle.api.file.DuplicatesStrategy -import org.junit.jupiter.api.Test - -class ShadowJarCachingTest : BaseCachingTest() { - /** - * Ensure that a basic usage reuses an output from cache and then gets a cache miss when the content changes. - */ - @Test - fun dependenciesChanged() { - projectScriptPath.appendText( - """ - dependencies { - ${implementationFiles(artifactAJar, artifactBJar)} - } - """.trimIndent(), - ) - - assertCompositeExecutions { - containsOnly( - *entriesInAB, - *manifestEntries, - ) - } - - val replaced = projectScriptPath.readText().lines() - .filterNot { it == implementationFiles(artifactBJar) } - .joinToString(lineSeparator) - projectScriptPath.writeText(replaced) - - assertCompositeExecutions { - containsOnly( - *entriesInA, - *manifestEntries, - ) - } - } - - @Test - fun outputFileChanged() { - projectScriptPath.appendText( - """ - dependencies { - ${implementationFiles(artifactAJar, artifactBJar)} - } - """.trimIndent() + lineSeparator, - ) - - assertCompositeExecutions { - containsOnly( - *entriesInAB, - *manifestEntries, - ) - } - - projectScriptPath.appendText( - """ - $shadowJar { - archiveBaseName = "foo" - } - """.trimIndent(), - ) - - assertExecutionsFromCacheAndUpToDate() - assertThat(jarPath("build/libs/foo-1.0-all.jar")).useAll { - containsOnly( - *entriesInAB, - *manifestEntries, - ) - } - } - - @Test - fun dependencyFilterChanged() { - publishArtifactCD() - projectScriptPath.appendText( - """ - dependencies { - implementation 'my:d:1.0' - } - """.trimIndent() + lineSeparator, - ) - val assertions = { - assertCompositeExecutions { - containsOnly( - "c.properties", - "d.properties", - *manifestEntries, - ) - } - } - - assertions() - - projectScriptPath.appendText( - """ - $shadowJar { - dependencyFilter = new ${MinimizeDependencyFilter::class.java.name}(project) - } - """.trimIndent(), - ) - - assertions() - } - - @Test - fun duplicatesStrategyChanged() { - listOf( - DuplicatesStrategy.EXCLUDE, - DuplicatesStrategy.INCLUDE, - DuplicatesStrategy.WARN, - ).forEach { strategy -> - projectScriptPath.writeText( - getDefaultProjectBuildScript(withGroup = true, withVersion = true) + - """ - $shadowJar { - duplicatesStrategy = DuplicatesStrategy.$strategy - } - """.trimIndent(), - ) - - assertCompositeExecutions() - } - } - - @Test - fun manifestAttrsChanged() { - projectScriptPath.appendText( - """ - jar { - manifest { - attributes 'Foo': 'Foo1' - } - } - $shadowJar { - manifest { - attributes 'Bar': 'Bar1' - } - } - """.trimIndent(), - ) - - val assertions = { valueFoo: String, valueBar: String -> - assertCompositeExecutions { - getMainAttr("Foo").isEqualTo(valueFoo) - getMainAttr("Bar").isEqualTo(valueBar) - } - } - - assertions("Foo1", "Bar1") - - var replaced = projectScriptPath.readText().replace("Foo1", "Foo2") - projectScriptPath.writeText(replaced) - - assertions("Foo2", "Bar1") - - replaced = projectScriptPath.readText().replace("Bar1", "Bar2") - projectScriptPath.writeText(replaced) - - assertions("Foo2", "Bar2") - - replaced = projectScriptPath.readText() - .replace("Foo2", "Foo3") - .replace("Bar2", "Bar3") - projectScriptPath.writeText(replaced) - - assertions("Foo3", "Bar3") - } - - @Test - fun kotlinMainRunChanged() { - val mainClassName = "my.Main" - val main2ClassName = "my.Main2" - - val projectBuildScript = getDefaultProjectBuildScript( - plugin = "org.jetbrains.kotlin.multiplatform", - withGroup = true, - withVersion = true, - ) - projectScriptPath.writeText( - """ - $projectBuildScript - kotlin { - jvm().mainRun { - it.mainClass.set('$mainClassName') - } - } - """.trimIndent(), - ) - - assertCompositeExecutions { - getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) - } - - val replaced = projectScriptPath.readText().replace(mainClassName, main2ClassName) - projectScriptPath.writeText(replaced) - - assertCompositeExecutions { - getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) - } - } - - @Test - fun applicationChanged() { - val mainClassName = "my.Main" - val main2ClassName = "my.Main2" - - projectScriptPath.appendText( - """ - apply plugin: 'application' - application { - mainClass = '$mainClassName' - } - """.trimIndent(), - ) - - assertCompositeExecutions { - getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) - } - - val replaced = projectScriptPath.readText().replace(mainClassName, main2ClassName) - projectScriptPath.writeText(replaced) - - assertCompositeExecutions { - getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) - } - } -} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt deleted file mode 100644 index 91a20044d..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/caching/TransformerCachingTest.kt +++ /dev/null @@ -1,79 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow.caching - -import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import kotlin.io.path.appendText -import kotlin.io.path.readText -import kotlin.io.path.writeText -import org.junit.jupiter.api.Test - -class TransformerCachingTest : BaseCachingTest() { - @Test - fun serviceFileTransformerPropsChanged() { - val mainClassEntry = writeClass() - val assertions = { - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *manifestEntries, - ) - } - } - - assertions() - - projectScriptPath.appendText( - transform( - transformerBlock = """ - path = 'META-INF/foo' - """.trimIndent(), - ), - ) - - assertions() - - val replaced = projectScriptPath.readText().replace("META-INF/foo", "META-INF/bar") - projectScriptPath.writeText(replaced) - - assertions() - } - - @Test - fun disableCacheIfAnyTransformerIsNotCacheable() { - projectScriptPath.appendText( - """ - $shadowJar { - mergeServiceFiles() - } - """.trimIndent() + lineSeparator, - ) - - assertCompositeExecutions() - - projectScriptPath.appendText( - """ - $shadowJar { - mergeGroovyExtensionModules() - } - """.trimIndent() + lineSeparator, - ) - - assertCompositeExecutions() - - projectScriptPath.appendText( - """ - $shadowJar { - // Use Transformer.Companion (no-op) to mock a custom transformer here, it's not cacheable. - transform(${ResourceTransformer.Companion::class.java.name}) - } - """.trimIndent(), - ) - - assertExecutionSuccess() - cleanOutputs() - // The shadowJar task should be executed again as the cache is disabled. - assertExecutionSuccess() - } -} From 5ae61f30e0e9fb3a9be46fd13fc32f0284a97d47 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 8 Aug 2025 03:42:25 +0800 Subject: [PATCH 628/941] Reduce test matrix (#1609) --- .github/workflows/build.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e69643f18..ca47a9e30 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,15 +12,20 @@ jobs: name: OS=${{ matrix.os }}, Java=${{ matrix.java }}, Gradle=${{ matrix.gradle }} strategy: matrix: - os: [ ubuntu-latest, windows-latest ] + os: [ ubuntu-latest ] # Always test on the latest version and some LTS. - java: [ 17, 21, 24 ] + java: [ 17, 24 ] # Test on the minimum Gradle version and the latest. gradle: [ 8.11, current ] exclude: # Gradle 8.11 doesn't support Java 24. - gradle: 8.11 java: 24 + include: + # We just test one JDK version on Windows. + - os: windows-latest + gradle: current + java: 21 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 From 8792466a365f085c61704b90380ee799f2a24297 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 8 Aug 2025 03:46:15 +0800 Subject: [PATCH 629/941] Try out ubuntu-24.04-arm runners (#1608) https://github.blog/changelog/2025-08-07-arm64-hosted-runners-for-public-repositories-are-now-generally-available/ --- .github/workflows/build.yml | 4 ++-- .github/workflows/deploy.yml | 2 +- .github/workflows/links.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/update-start-scripts.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ca47a9e30..0ecec76cd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: name: OS=${{ matrix.os }}, Java=${{ matrix.java }}, Gradle=${{ matrix.gradle }} strategy: matrix: - os: [ ubuntu-latest ] + os: [ ubuntu-24.04-arm ] # Always test on the latest version and some LTS. java: [ 17, 24 ] # Test on the minimum Gradle version and the latest. @@ -38,7 +38,7 @@ jobs: publish-snapshot: needs: build - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm if: github.event.repository.fork == false && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3cb23261e..390183dfb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -6,7 +6,7 @@ on: jobs: deploy: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm if: github.event.repository.fork == false environment: name: github-pages diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index 5a5c6c5c6..d752a2e77 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -19,7 +19,7 @@ on: jobs: check-links: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@v4 - uses: gradle/actions/setup-gradle@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 56532ea16..e948245bf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,7 +7,7 @@ on: jobs: release: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm if: github.event.repository.fork == false environment: name: maven-central diff --git a/.github/workflows/update-start-scripts.yml b/.github/workflows/update-start-scripts.yml index b61bc0260..492e10d24 100644 --- a/.github/workflows/update-start-scripts.yml +++ b/.github/workflows/update-start-scripts.yml @@ -11,7 +11,7 @@ on: jobs: update-start-scripts: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm if: github.event.repository.fork == false steps: - uses: actions/checkout@v4 From 4e73154dd1ee1c85c2ade8085b66f7ddf57cdcf9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 8 Aug 2025 10:01:23 +0800 Subject: [PATCH 630/941] Add build-status workflow (#1612) --- .github/workflows/build.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0ecec76cd..ec1c988da 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,6 +36,14 @@ jobs: - uses: gradle/actions/setup-gradle@v4 - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" + # Status check that is required in branch protection rules. + build-status: + needs: build + runs-on: ubuntu-24.04-arm + steps: + - run: | + echo all build jobs completed successfully. + publish-snapshot: needs: build runs-on: ubuntu-24.04-arm From 8f9c734be90ed83a27541c5d83f9292e26df470a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 8 Aug 2025 10:47:25 +0800 Subject: [PATCH 631/941] Functional test cleanups (#1604) --- .../plugins/shadow/AndroidPluginTest.kt | 2 +- .../plugins/shadow/ApplicationPluginTest.kt | 22 +-- .../gradle/plugins/shadow/BasePluginTest.kt | 64 ++++--- .../gradle/plugins/shadow/CachingTest.kt | 113 ++++++------- .../gradle/plugins/shadow/FilteringTest.kt | 56 +++--- .../gradle/plugins/shadow/GroovyPluginTest.kt | 8 +- .../{JavaPluginTest.kt => JavaPluginsTest.kt} | 160 +++++++++--------- .../plugins/shadow/KotlinPluginsTest.kt | 110 ++++++------ .../{MinimizationTest.kt => MinimizeTest.kt} | 34 ++-- .../gradle/plugins/shadow/PublishingTest.kt | 28 +-- .../gradle/plugins/shadow/RelocationTest.kt | 112 ++++++------ .../gradle/plugins/shadow/ScalaPluginTest.kt | 8 +- .../transformers/AppendingTransformerTest.kt | 18 +- .../GroovyExtensionModuleTransformerTest.kt | 12 +- .../PropertiesFileTransformerTest.kt | 30 ++-- .../ServiceFileTransformerTest.kt | 50 +++--- .../shadow/transformers/TransformersTest.kt | 60 +++---- .../XmlAppendingTransformerTest.kt | 12 +- 18 files changed, 447 insertions(+), 452 deletions(-) rename src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{JavaPluginTest.kt => JavaPluginsTest.kt} (89%) rename src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/{MinimizationTest.kt => MinimizeTest.kt} (91%) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt index 9800c9d60..0357eeed0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt @@ -10,7 +10,7 @@ class AndroidPluginTest : BasePluginTest() { @ParameterizedTest @MethodSource("androidIdsProvider") fun doNotCompatAgp(pluginId: String) { - projectScriptPath.writeText(getDefaultProjectBuildScript(plugin = pluginId)) + projectScript.writeText(getDefaultProjectBuildScript(plugin = pluginId)) assertThat(runWithFailure().output).contains( "Shadow does not support using with AGP, you may need Android Fused Library plugin instead.", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index ef7d1a14c..c86527879 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -54,7 +54,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - val result = run(runShadowTask) + val result = run(runShadowPath) assertThat(result.output).contains( "Running application with JDK 17", @@ -71,7 +71,7 @@ class ApplicationPluginTest : BasePluginTest() { applicationBlock = "applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED']", ) - run(installShadowDistTask) + run(installShadowDistPath) val installPath = path("build/install/") assertThat(installPath.walkEntries()).containsOnly( @@ -113,7 +113,7 @@ class ApplicationPluginTest : BasePluginTest() { fun installShadowDoesNotExecuteDependentShadowTask() { prepare() - run(installShadowDistTask) + run(installShadowDistPath) commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) } @@ -141,14 +141,14 @@ class ApplicationPluginTest : BasePluginTest() { } } - assertions(run(runShadowTask, shadowJarTask).output, "foo") + assertions(run(runShadowPath).output, "foo") commonAssertions( jarPath("build/libs/myapp-1.0-all.jar"), entriesContained = entriesInA + arrayOf(mainClass, main2ClassEntry), mainClassAttr = "my.Main2", ) - projectScriptPath.appendText( + projectScript.appendText( """ run { args 'bar' @@ -162,7 +162,7 @@ class ApplicationPluginTest : BasePluginTest() { fun errorWhenMainClassNotSet() { prepare(mainClassBlock = "") - val result = runWithFailure(runShadowTask) + val result = runWithFailure(runShadowPath) assertThat(result.output).contains( "The main class must be specified and not left empty in `application.mainClass` or manifest attributes.", @@ -188,7 +188,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowDistZipTask) + run(shadowDistZipPath) val zipPath = path("build/distributions/myapp-shadow-1.0.zip") ZipFile(zipPath.toFile()).use { zip -> @@ -212,7 +212,7 @@ class ApplicationPluginTest : BasePluginTest() { path("src/dist/echo.sh").writeText("echo 'Hello, World!'") prepare() - run(shadowDistZipTask) + run(shadowDistZipPath) val zipPath = path("build/distributions/myapp-shadow-1.0.zip") ZipFile(zipPath.toFile()).use { zip -> @@ -238,7 +238,7 @@ class ApplicationPluginTest : BasePluginTest() { runShadowBlock: String = "", ) { mainClass = writeClass(withImports = mainClassWithImports) - projectScriptPath.appendText( + projectScript.appendText( """ apply plugin: 'application' $projectBlock @@ -249,13 +249,13 @@ class ApplicationPluginTest : BasePluginTest() { dependencies { $dependenciesBlock } - $runShadow { + $runShadowTask { args 'foo' $runShadowBlock } """.trimIndent() + lineSeparator, ) - settingsScriptPath.writeText( + settingsScript.writeText( getDefaultSettingsBuildScript( startBlock = settingsBlock, endBlock = "rootProject.name = 'myapp'", 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 5ccd3fb90..be37ed3b3 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 @@ -47,24 +47,31 @@ import org.junit.jupiter.api.io.TempDir abstract class BasePluginTest { @TempDir lateinit var projectRoot: Path + private set lateinit var localRepo: AppendableMavenRepository + private set lateinit var artifactAJar: Path + private set lateinit var artifactBJar: Path + private set lateinit var entriesInA: Array + private set lateinit var entriesInB: Array + private set lateinit var entriesInAB: Array + private set - val shadowJarTask = ":$SHADOW_JAR_TASK_NAME" - val serverShadowJarTask = ":server:$SHADOW_JAR_TASK_NAME" - val runShadowTask = ":$SHADOW_RUN_TASK_NAME" - val installShadowDistTask = ":$SHADOW_INSTALL_TASK_NAME" - val shadowDistZipTask = ":shadowDistZip" + val shadowJarPath = ":$SHADOW_JAR_TASK_NAME" + val serverShadowJarPath = ":server:$SHADOW_JAR_TASK_NAME" + val runShadowPath = ":$SHADOW_RUN_TASK_NAME" + val installShadowDistPath = ":$SHADOW_INSTALL_TASK_NAME" + val shadowDistZipPath = ":shadowDistZip" - val projectScriptPath: Path get() = path("build.gradle") - val settingsScriptPath: Path get() = path("settings.gradle") - open val outputShadowJar: JarPath get() = jarPath("build/libs/my-1.0-all.jar") - val outputServerShadowJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") + val projectScript: Path get() = path("build.gradle") + val settingsScript: Path get() = path("settings.gradle") + open val outputShadowedJar: JarPath get() = jarPath("build/libs/my-1.0-all.jar") + val outputServerShadowedJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") @BeforeAll open fun doFirst() { @@ -94,13 +101,13 @@ abstract class BasePluginTest { @BeforeEach open fun setup() { - projectScriptPath.writeText(getDefaultProjectBuildScript(withGroup = true, withVersion = true)) - settingsScriptPath.writeText(getDefaultSettingsBuildScript()) + projectScript.writeText(getDefaultProjectBuildScript(withGroup = true, withVersion = true)) + settingsScript.writeText(getDefaultSettingsBuildScript()) } @AfterEach fun cleanup() { - println(projectScriptPath.readText()) + println(projectScript.readText()) } @AfterAll @@ -172,17 +179,19 @@ abstract class BasePluginTest { } fun run( - vararg tasks: String, - runnerBlock: (GradleRunner) -> GradleRunner = { it }, + vararg arguments: String, + runnerBlock: (GradleRunner) -> Unit = {}, ): BuildResult { - return runnerBlock(runner(tasks.toList())).build().assertNoDeprecationWarnings() + return runner(arguments = arguments.toList(), block = runnerBlock) + .build().assertNoDeprecationWarnings() } fun runWithFailure( - vararg tasks: String, - runnerBlock: (GradleRunner) -> GradleRunner = { it }, + vararg arguments: String, + runnerBlock: (GradleRunner) -> Unit = {}, ): BuildResult { - return runnerBlock(runner(tasks.toList())).buildAndFail().assertNoDeprecationWarnings() + return runner(arguments = arguments.toList(), block = runnerBlock) + .buildAndFail().assertNoDeprecationWarnings() } fun publishArtifactCD(circular: Boolean = false) { @@ -270,12 +279,12 @@ abstract class BasePluginTest { clientShadowed: Boolean = false, serverShadowBlock: String = "", ) { - settingsScriptPath.appendText( + settingsScript.appendText( """ include 'client', 'server' """.trimIndent(), ) - projectScriptPath.writeText("") + projectScript.writeText("") path("client/src/main/java/client/Client.java").writeText( """ @@ -305,7 +314,7 @@ abstract class BasePluginTest { dependencies { implementation project(':client') } - $shadowJar { + $shadowJarTask { $serverShadowBlock } """.trimIndent() + lineSeparator, @@ -314,7 +323,7 @@ abstract class BasePluginTest { if (!clientShadowed) return path("client/build.gradle").appendText( """ - $shadowJar { + $shadowJarTask { relocate 'junit.framework', 'client.junit.framework' } """.trimIndent() + lineSeparator, @@ -354,7 +363,7 @@ abstract class BasePluginTest { """.trimIndent() } - projectScriptPath.writeText( + projectScript.writeText( """ ${getDefaultProjectBuildScript("java-gradle-plugin", withGroup = true, withVersion = true)} $gradlePluginBlock @@ -378,6 +387,7 @@ abstract class BasePluginTest { fun runner( arguments: Iterable = emptyList(), projectDir: Path? = projectRoot, + block: (GradleRunner) -> Unit = {}, ): GradleRunner = GradleRunner.create() .withGradleVersion(testGradleVersion) .forwardOutput() @@ -388,6 +398,7 @@ abstract class BasePluginTest { if (projectDir != null) { withProjectDir(projectDir.toFile()) } + block(this) } @Suppress("ConstPropertyName") @@ -416,8 +427,9 @@ abstract class BasePluginTest { const val manifestEntry = "META-INF/MANIFEST.MF" val manifestEntries = arrayOf("META-INF/", manifestEntry) - val shadowJar: String = "tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name})" - const val runShadow = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)" + val shadowJarTask: String = "tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name})" + const val runShadowTask = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)" + const val jarTask = "tasks.named('jar', Jar)" val commonArguments = listOf( "--warning-mode=fail", @@ -449,7 +461,7 @@ abstract class BasePluginTest { dependencies { $dependenciesBlock } - $shadowJar { + $shadowJarTask { transform(${T::class.java.name}) { $transformerBlock } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt index 5742e161c..ca84a2b28 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -1,12 +1,9 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.Assert -import assertk.assertFailure import assertk.assertThat -import assertk.assertions.contains import assertk.assertions.isEmpty import assertk.assertions.isEqualTo -import assertk.assertions.isInstanceOf import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer @@ -27,14 +24,14 @@ import org.gradle.testkit.runner.TaskOutcome import org.junit.jupiter.api.Test class CachingTest : BasePluginTest() { - private var taskPath: String = shadowJarTask + private var taskPath: String = shadowJarPath /** * Ensure that a basic usage reuses an output from cache and then gets a cache miss when the content changes. */ @Test fun dependenciesChanged() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(artifactAJar, artifactBJar)} @@ -49,10 +46,8 @@ class CachingTest : BasePluginTest() { ) } - val replaced = projectScriptPath.readText().lines() - .filterNot { it == implementationFiles(artifactBJar) } - .joinToString(lineSeparator) - projectScriptPath.writeText(replaced) + val replaced = projectScript.readText().replace(implementationFiles(artifactBJar), "") + projectScript.writeText(replaced) assertCompositeExecutions { containsOnly( @@ -64,7 +59,7 @@ class CachingTest : BasePluginTest() { @Test fun outputFileChanged() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(artifactAJar, artifactBJar)} @@ -79,9 +74,9 @@ class CachingTest : BasePluginTest() { ) } - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { archiveBaseName = "foo" } """.trimIndent(), @@ -99,7 +94,7 @@ class CachingTest : BasePluginTest() { @Test fun dependencyFilterChanged() { publishArtifactCD() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:d:1.0' @@ -118,9 +113,9 @@ class CachingTest : BasePluginTest() { assertions() - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { dependencyFilter = new ${MinimizeDependencyFilter::class.java.name}(project) } """.trimIndent(), @@ -136,10 +131,10 @@ class CachingTest : BasePluginTest() { DuplicatesStrategy.INCLUDE, DuplicatesStrategy.WARN, ).forEach { strategy -> - projectScriptPath.writeText( + projectScript.writeText( getDefaultProjectBuildScript(withGroup = true, withVersion = true) + """ - $shadowJar { + $shadowJarTask { duplicatesStrategy = DuplicatesStrategy.$strategy } """.trimIndent(), @@ -151,14 +146,14 @@ class CachingTest : BasePluginTest() { @Test fun manifestAttrsChanged() { - projectScriptPath.appendText( + projectScript.appendText( """ - jar { + $jarTask { manifest { attributes 'Foo': 'Foo1' } } - $shadowJar { + $shadowJarTask { manifest { attributes 'Bar': 'Bar1' } @@ -175,20 +170,20 @@ class CachingTest : BasePluginTest() { assertions("Foo1", "Bar1") - var replaced = projectScriptPath.readText().replace("Foo1", "Foo2") - projectScriptPath.writeText(replaced) + var replaced = projectScript.readText().replace("Foo1", "Foo2") + projectScript.writeText(replaced) assertions("Foo2", "Bar1") - replaced = projectScriptPath.readText().replace("Bar1", "Bar2") - projectScriptPath.writeText(replaced) + replaced = projectScript.readText().replace("Bar1", "Bar2") + projectScript.writeText(replaced) assertions("Foo2", "Bar2") - replaced = projectScriptPath.readText() + replaced = projectScript.readText() .replace("Foo2", "Foo3") .replace("Bar2", "Bar3") - projectScriptPath.writeText(replaced) + projectScript.writeText(replaced) assertions("Foo3", "Bar3") } @@ -203,7 +198,7 @@ class CachingTest : BasePluginTest() { withGroup = true, withVersion = true, ) - projectScriptPath.writeText( + projectScript.writeText( """ $projectBuildScript kotlin { @@ -218,8 +213,8 @@ class CachingTest : BasePluginTest() { getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) } - val replaced = projectScriptPath.readText().replace(mainClassName, main2ClassName) - projectScriptPath.writeText(replaced) + val replaced = projectScript.readText().replace(mainClassName, main2ClassName) + projectScript.writeText(replaced) assertCompositeExecutions { getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) @@ -231,7 +226,7 @@ class CachingTest : BasePluginTest() { val mainClassName = "my.Main" val main2ClassName = "my.Main2" - projectScriptPath.appendText( + projectScript.appendText( """ apply plugin: 'application' application { @@ -244,8 +239,8 @@ class CachingTest : BasePluginTest() { getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) } - val replaced = projectScriptPath.readText().replace(mainClassName, main2ClassName) - projectScriptPath.writeText(replaced) + val replaced = projectScript.readText().replace(mainClassName, main2ClassName) + projectScript.writeText(replaced) assertCompositeExecutions { getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) @@ -259,7 +254,7 @@ class CachingTest : BasePluginTest() { fun jarIncludesExcludesChanged() { val mainClassEntry = writeClass(className = "Main") val main2ClassEntry = writeClass(className = "Main2") - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:a:1.0' @@ -278,9 +273,9 @@ class CachingTest : BasePluginTest() { ) } - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { exclude '**.properties' } """.trimIndent() + lineSeparator, @@ -295,9 +290,9 @@ class CachingTest : BasePluginTest() { ) } - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { include '$mainClassEntry' } """.trimIndent() + lineSeparator, @@ -311,9 +306,9 @@ class CachingTest : BasePluginTest() { ) } - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { include '$main2ClassEntry' } """.trimIndent() + lineSeparator, @@ -332,7 +327,7 @@ class CachingTest : BasePluginTest() { @Test fun dependenciesIncludesExcludesChanged() { val mainClassEntry = writeClass(withImports = true) - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' @@ -349,9 +344,9 @@ class CachingTest : BasePluginTest() { ) } - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { dependencies { exclude(dependency('junit:junit')) } @@ -370,7 +365,7 @@ class CachingTest : BasePluginTest() { @Test fun minimizeChanged() { - taskPath = serverShadowJarTask + taskPath = serverShadowJarPath writeClientAndServerModules() path("server/src/main/java/server/Server.java").writeText( @@ -381,7 +376,7 @@ class CachingTest : BasePluginTest() { ) assertCompositeExecutions( - jarPathProvider = { outputServerShadowJar }, + jarPathProvider = { outputServerShadowedJar }, ) { containsOnly( "client/", @@ -395,7 +390,7 @@ class CachingTest : BasePluginTest() { path("server/build.gradle").appendText( """ - $shadowJar { + $shadowJarTask { minimize { exclude(dependency('junit:junit:.*')) } @@ -404,7 +399,7 @@ class CachingTest : BasePluginTest() { ) assertCompositeExecutions( - jarPathProvider = { outputServerShadowJar }, + jarPathProvider = { outputServerShadowedJar }, ) { containsOnly( "server/", @@ -420,7 +415,7 @@ class CachingTest : BasePluginTest() { */ @Test fun relocatorAdded() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' @@ -438,9 +433,9 @@ class CachingTest : BasePluginTest() { ) } - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { relocate 'junit.framework', 'foo.junit.framework' } """.trimIndent(), @@ -475,7 +470,7 @@ class CachingTest : BasePluginTest() { assertions() - projectScriptPath.appendText( + projectScript.appendText( transform( transformerBlock = """ path = 'META-INF/foo' @@ -485,17 +480,17 @@ class CachingTest : BasePluginTest() { assertions() - val replaced = projectScriptPath.readText().replace("META-INF/foo", "META-INF/bar") - projectScriptPath.writeText(replaced) + val replaced = projectScript.readText().replace("META-INF/foo", "META-INF/bar") + projectScript.writeText(replaced) assertions() } @Test fun disableCacheIfAnyTransformerIsNotCacheable() { - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { mergeServiceFiles() } """.trimIndent() + lineSeparator, @@ -503,9 +498,9 @@ class CachingTest : BasePluginTest() { assertCompositeExecutions() - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { mergeGroovyExtensionModules() } """.trimIndent() + lineSeparator, @@ -513,9 +508,9 @@ class CachingTest : BasePluginTest() { assertCompositeExecutions() - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { // Use Transformer.Companion (no-op) to mock a custom transformer here, it's not cacheable. transform(${ResourceTransformer.Companion::class.java.name}) } @@ -556,7 +551,7 @@ class CachingTest : BasePluginTest() { * Combines [assertExecutionSuccess] and [assertExecutionsFromCacheAndUpToDate] for simplifying assertions. */ private fun assertCompositeExecutions( - jarPathProvider: () -> JarPath = { outputShadowJar }, + jarPathProvider: () -> JarPath = { outputShadowedJar }, jarPathAssertions: Assert.() -> Unit = {}, ) { // First run should execute. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 0dc2a2e31..cdadc8ec7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -21,7 +21,7 @@ class FilteringTest : BasePluginTest() { @BeforeEach override fun setup() { super.setup() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:a:1.0' @@ -33,9 +33,9 @@ class FilteringTest : BasePluginTest() { @Test fun includeAllDependencies() { - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( *entriesInAB, *manifestEntries, @@ -45,17 +45,17 @@ class FilteringTest : BasePluginTest() { @Test fun excludeFiles() { - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { exclude 'a2.properties' } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "a.properties", "b.properties", @@ -69,7 +69,7 @@ class FilteringTest : BasePluginTest() { fun excludeDependency(useAccessor: Boolean) { dependOnAndExcludeArtifactD(useAccessor) - run(shadowJarTask) + run(shadowJarPath) commonAssertions() } @@ -77,12 +77,12 @@ class FilteringTest : BasePluginTest() { @ParameterizedTest @MethodSource("wildcardDepProvider") fun excludeDependencyUsingWildcardSyntax(wildcard: String) { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:d:1.0' } - $shadowJar { + $shadowJarTask { dependencies { exclude(dependency('$wildcard')) } @@ -90,19 +90,19 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) commonAssertions() } @Test fun includeDependencyAndExcludeOthers() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:d:1.0' } - $shadowJar { + $shadowJarTask { dependencies { include(dependency('my:d:1.0')) } @@ -116,9 +116,9 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "d.properties", "my/", @@ -140,9 +140,9 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { containsOnly( "server/", "server/Server.class", @@ -162,9 +162,9 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { containsOnly( "client/", "server/", @@ -177,9 +177,9 @@ class FilteringTest : BasePluginTest() { @Test fun verifyExcludePrecedenceOverInclude() { - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { include '*.jar' include '*.properties' exclude 'a2.properties' @@ -187,9 +187,9 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "a.properties", "b.properties", @@ -203,13 +203,13 @@ class FilteringTest : BasePluginTest() { publishArtifactCD(circular = true) dependOnAndExcludeArtifactD() - run(shadowJarTask) + run(shadowJarPath) commonAssertions() } private fun dependOnAndExcludeArtifactD(useAccessor: Boolean = false) { - settingsScriptPath.appendText( + settingsScript.appendText( """ dependencyResolutionManagement { versionCatalogs.create('libs') { @@ -219,12 +219,12 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) val dependency = if (useAccessor) "libs.my.d" else "'my:d:1.0'" - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation $dependency } - $shadowJar { + $shadowJarTask { dependencies { exclude(dependency($dependency)) } @@ -234,7 +234,7 @@ class FilteringTest : BasePluginTest() { } private fun commonAssertions() { - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "c.properties", *entriesInAB, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt index 6aacb7555..7c85e8a07 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt @@ -17,13 +17,13 @@ class GroovyPluginTest : BasePluginTest() { withGroup = true, withVersion = true, ) - projectScriptPath.writeText(projectBuildScript) + projectScript.writeText(projectBuildScript) } @Test fun compatGroovy() { val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Groovy) - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { compileOnly localGroovy() @@ -32,9 +32,9 @@ class GroovyPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "my/", mainClassEntry, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt similarity index 89% rename from src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index ce8cd9727..d2c17cac5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -48,7 +48,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -class JavaPluginTest : BasePluginTest() { +class JavaPluginsTest : BasePluginTest() { @Test fun applyPlugin() { val projectName = "my-shadow" @@ -105,7 +105,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun shadowJarCliOptions() { - val result = run("help", "--task", shadowJarTask) + val result = run("help", "--task", shadowJarPath) assertThat(result.output).contains( "--enable-auto-relocation Enables auto relocation of packages in the dependencies.", @@ -122,9 +122,9 @@ class JavaPluginTest : BasePluginTest() { fun includeProjectDependencies() { writeClientAndServerModules() - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { containsOnly( "client/", "server/", @@ -167,9 +167,9 @@ class JavaPluginTest : BasePluginTest() { val relocatedEntries = junitEntries .map { it.replace("junit/framework/", "client/junit/framework/") }.toTypedArray() - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { containsOnly( "client/", "server/", @@ -199,7 +199,7 @@ class JavaPluginTest : BasePluginTest() { writeClientAndServerModules() path("client/build.gradle").appendText( """ - jar { + $jarTask { manifest { attributes '$multiReleaseAttributeKey': 'true' } @@ -207,9 +207,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent() + lineSeparator, ) - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { transform { it.mainAttrSize }.isGreaterThan(1) getMainAttr(multiReleaseAttributeKey).isEqualTo("true") } @@ -241,7 +241,7 @@ class JavaPluginTest : BasePluginTest() { public class Passed {} """.trimIndent(), ) - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:a:1.0' @@ -249,9 +249,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "my/", "my/Passed.class", @@ -264,7 +264,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun includeRuntimeConfigurationByDefault() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { runtimeOnly 'my:a:1.0' @@ -273,9 +273,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( *entriesInA, *manifestEntries, @@ -304,7 +304,7 @@ class JavaPluginTest : BasePluginTest() { } }.publish() - projectScriptPath.writeText( + projectScript.writeText( """ ${getDefaultProjectBuildScript("java-library", withGroup = true, withVersion = true)} dependencies { @@ -315,9 +315,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "api.properties", "implementation.properties", @@ -330,7 +330,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun doNotIncludeCompileOnlyConfigurationByDefault() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { runtimeOnly 'my:a:1.0' @@ -339,9 +339,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( *entriesInA, *manifestEntries, @@ -361,7 +361,7 @@ class JavaPluginTest : BasePluginTest() { } }.publish() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { runtimeOnly 'my:a:1.0' @@ -370,15 +370,15 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - val entries = outputShadowJar.use { it.entries().toList() } + val entries = outputShadowedJar.use { it.entries().toList() } assertThat(entries.size).isEqualTo(2) } @Test fun classPathInManifestNotAddedIfEmpty() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' @@ -386,9 +386,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - val value = outputShadowJar.use { it.getMainAttr(classPathAttributeKey) } + val value = outputShadowedJar.use { it.getMainAttr(classPathAttributeKey) } assertThat(value).isNull() } @@ -397,12 +397,12 @@ class JavaPluginTest : BasePluginTest() { ) @Test fun addShadowConfigurationToClassPathInManifest() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { shadow 'junit:junit:3.8.2' } - jar { + $jarTask { manifest { attributes '$classPathAttributeKey': '/libs/a.jar' } @@ -410,9 +410,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - val value = outputShadowJar.use { it.getMainAttr(classPathAttributeKey) } + val value = outputShadowedJar.use { it.getMainAttr(classPathAttributeKey) } assertThat(value).isEqualTo("/libs/a.jar junit-3.8.2.jar") } @@ -421,7 +421,7 @@ class JavaPluginTest : BasePluginTest() { ) @Test fun doNotIncludeNullValueInClassPathWhenJarFileDoesNotContainClassPath() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { shadow 'junit:junit:3.8.2' @@ -429,9 +429,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - val value = outputShadowJar.use { it.getMainAttr(classPathAttributeKey) } + val value = outputShadowedJar.use { it.getMainAttr(classPathAttributeKey) } assertThat(value).isEqualTo("junit-3.8.2.jar") } @@ -440,21 +440,21 @@ class JavaPluginTest : BasePluginTest() { ) @Test fun supportZipCompressionStored() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { shadow 'junit:junit:3.8.2' } - $shadowJar { + $shadowJarTask { zip64 = true entryCompression = org.gradle.api.tasks.bundling.ZipEntryCompression.STORED } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { transform { it.entries().toList() }.isNotEmpty() } } @@ -467,7 +467,7 @@ class JavaPluginTest : BasePluginTest() { @ValueSource(booleans = [false, true]) fun excludeGradleApiByDefault(legacy: Boolean) { writeGradlePluginModule(legacy) - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:a:1.0' @@ -476,9 +476,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { transform { actual -> actual.entries().toList().map { it.name }.filter { it.endsWith(".class") } } .single().isEqualTo("my/plugin/MyPlugin.class") transform { it.mainAttrSize }.isGreaterThan(0) @@ -502,7 +502,7 @@ class JavaPluginTest : BasePluginTest() { ) @Test fun movesLocalGradleApiToCompileOnly() { - projectScriptPath.writeText( + projectScript.writeText( """ ${getDefaultProjectBuildScript("java-gradle-plugin")} """.trimIndent() + lineSeparator, @@ -522,7 +522,7 @@ class JavaPluginTest : BasePluginTest() { @ParameterizedTest @ValueSource(strings = [COMPILE_ONLY_CONFIGURATION_NAME, API_CONFIGURATION_NAME]) fun doNotReAddSuppressedGradleApi(configuration: String) { - projectScriptPath.writeText( + projectScript.writeText( """ ${getDefaultProjectBuildScript("java-gradle-plugin")} """.trimIndent() + lineSeparator, @@ -545,7 +545,7 @@ class JavaPluginTest : BasePluginTest() { fun registerCustomShadowJarTask() { val mainClassEntry = writeClass(sourceSet = "test", withImports = true) val testShadowJarTask = "testShadowJar" - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { testImplementation 'junit:junit:3.8.2' @@ -590,7 +590,7 @@ class JavaPluginTest : BasePluginTest() { val mainClassEntry = writeClass() val dependencyShadowJar = "dependencyShadowJar" - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' @@ -631,7 +631,7 @@ class JavaPluginTest : BasePluginTest() { writeText("A bad jar.") } - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(badJarPath)} @@ -639,7 +639,7 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - val result = runWithFailure(shadowJarTask) + val result = runWithFailure(shadowJarPath) assertThat(result.output).containsMatch("Cannot expand ZIP '.*bad\\.jar'".toRegex()) } @@ -648,7 +648,7 @@ class JavaPluginTest : BasePluginTest() { fun failBuildIfProcessingAar() { val fooAarPath = path("foo.aar") - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(fooAarPath)} @@ -656,7 +656,7 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - val result = runWithFailure(shadowJarTask) + val result = runWithFailure(shadowJarPath) assertThat(result.output).contains( "Shadowing AAR file is not supported.", @@ -667,18 +667,18 @@ class JavaPluginTest : BasePluginTest() { @Test fun worksWithArchiveFileName() { val mainClassEntry = writeClass() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } - $shadowJar { + $shadowJarTask { archiveFileName = 'my-shadow.tar' } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) assertThat(jarPath("build/libs/my-shadow.tar")).useAll { containsOnly( @@ -692,9 +692,9 @@ class JavaPluginTest : BasePluginTest() { @Test fun inheritFromOtherManifest() { - projectScriptPath.appendText( + projectScript.appendText( """ - jar { + $jarTask { manifest { attributes 'Foo-Attr': 'Foo-Value' } @@ -704,15 +704,15 @@ class JavaPluginTest : BasePluginTest() { attributes 'Bar-Attr': 'Bar-Value' } } - $shadowJar { + $shadowJarTask { manifest.inheritFrom(testJar.get().manifest) } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { transform { it.mainAttrSize }.isGreaterThan(2) getMainAttr("Foo-Attr").isEqualTo("Foo-Value") getMainAttr("Bar-Attr").isEqualTo("Bar-Value") @@ -723,9 +723,9 @@ class JavaPluginTest : BasePluginTest() { fun addExtraFilesViaFrom() { val mainClassEntry = writeClass() path("Foo").writeText("Foo") - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { from(file('${artifactAJar.invariantSeparatorsPathString}')) { into('META-INF') } @@ -736,9 +736,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "my/", "Bar/", @@ -750,7 +750,7 @@ class JavaPluginTest : BasePluginTest() { getContent("Bar/Foo").isEqualTo("Foo") } val unzipped = path("unzipped") - outputShadowJar.use { + outputShadowedJar.use { it.getStream("META-INF/a-1.0.jar").use { inputStream -> inputStream.copyTo(unzipped.outputStream()) } @@ -772,12 +772,12 @@ class JavaPluginTest : BasePluginTest() { writeClass(className = "module-info") { "module myModuleName {}" } - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(fooJar)} } - $shadowJar { + $shadowJarTask { duplicatesStrategy = DuplicatesStrategy.EXCLUDE excludes.remove( 'module-info.class' @@ -786,9 +786,9 @@ class JavaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "module-info.class", "my/", @@ -809,7 +809,7 @@ class JavaPluginTest : BasePluginTest() { @Test fun includeFilesInTaskOutputDirectory() { // Create a build that has a task with jars in the output directory - projectScriptPath.appendText( + projectScript.appendText( """ def createJars = tasks.register('createJars') { def artifactAJar = file('${artifactAJar.invariantSeparatorsPathString}') @@ -830,15 +830,15 @@ class JavaPluginTest : BasePluginTest() { } } } - $shadowJar { + $shadowJarTask { includedDependencies.from(files(createJars).asFileTree) } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly(*entriesInAB, *manifestEntries) } } @@ -846,17 +846,17 @@ class JavaPluginTest : BasePluginTest() { @Test fun integrateWithDevelocityBuildScan() { writeClientAndServerModules() - settingsScriptPath.writeText( + settingsScript.writeText( """ plugins { id 'com.gradle.develocity' } - ${settingsScriptPath.readText()} + ${settingsScript.readText()} """.trimIndent(), ) val result = run( - serverShadowJarTask, + serverShadowJarPath, IP_ARGUMENT, "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a file instead. @@ -876,21 +876,21 @@ class JavaPluginTest : BasePluginTest() { @ValueSource(booleans = [false, true]) fun failBuildIfDuplicateEntries(enable: Boolean) { path("src/main/resources/a.properties").writeText("invalid a") - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(artifactAJar)} } - $shadowJar { + $shadowJarTask { failOnDuplicateEntries = $enable } """.trimIndent(), ) val result = if (enable) { - runWithFailure(shadowJarTask) + runWithFailure(shadowJarPath) } else { - run(shadowJarTask, INFO_ARGUMENT) + run(shadowJarPath, INFO_ARGUMENT) } assertThat(result.output).contains( @@ -903,7 +903,7 @@ class JavaPluginTest : BasePluginTest() { @ValueSource(booleans = [false, true]) fun failBuildIfDuplicateEntriesByCliOption(enable: Boolean) { path("src/main/resources/a.properties").writeText("invalid a") - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(artifactAJar)} @@ -912,9 +912,9 @@ class JavaPluginTest : BasePluginTest() { ) val result = if (enable) { - runWithFailure(shadowJarTask, "--fail-on-duplicate-entries") + runWithFailure(shadowJarPath, "--fail-on-duplicate-entries") } else { - run(shadowJarTask, INFO_ARGUMENT) + run(shadowJarPath, INFO_ARGUMENT) } assertThat(result.output).contains( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 4d01053f8..09e00af32 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -26,7 +26,7 @@ class KotlinPluginsTest : BasePluginTest() { withGroup = true, withVersion = true, ) - projectScriptPath.writeText(projectBuildScript) + projectScript.writeText(projectBuildScript) } @ParameterizedTest @@ -34,7 +34,7 @@ class KotlinPluginsTest : BasePluginTest() { fun compatKotlinJvmPlugin(excludeStdlib: Boolean) { val stdlib = compileOnlyStdlib(excludeStdlib) - projectScriptPath.writeText( + projectScript.writeText( """ ${getDefaultProjectBuildScript(plugin = "org.jetbrains.kotlin.jvm", withGroup = true, withVersion = true)} dependencies { @@ -45,9 +45,9 @@ class KotlinPluginsTest : BasePluginTest() { ) val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Kotlin) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { val entries = arrayOf( "my/", "META-INF/my.kotlin_module", @@ -69,7 +69,7 @@ class KotlinPluginsTest : BasePluginTest() { val stdlib = compileOnlyStdlib(excludeStdlib) val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin) - projectScriptPath.appendText( + projectScript.appendText( """ kotlin { jvm() @@ -90,9 +90,9 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { val entries = arrayOf( "my/", "META-INF/my.kotlin_module", @@ -117,7 +117,7 @@ class KotlinPluginsTest : BasePluginTest() { val jvmTargetMain = "${jvmTargetName}Main" val stdlib = compileOnlyStdlib(true) val mainClassEntry = writeClass(sourceSet = jvmTargetMain, jvmLang = JvmLang.Kotlin) - projectScriptPath.appendText( + projectScript.appendText( """ kotlin { jvm('$jvmTargetName') @@ -138,9 +138,9 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { val entries = arrayOf( "my/", "META-INF/my.kotlin_module", @@ -157,7 +157,7 @@ class KotlinPluginsTest : BasePluginTest() { ) @Test fun doNotCreateJvmTargetEagerly() { - projectScriptPath.appendText( + projectScript.appendText( """ kotlin { mingwX64() @@ -165,7 +165,7 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = runWithFailure(shadowJarTask) + val result = runWithFailure(shadowJarPath) assertThat(result.output).contains( "Cannot locate tasks that match ':shadowJar' as task 'shadowJar' not found in root project", @@ -175,19 +175,17 @@ class KotlinPluginsTest : BasePluginTest() { @ParameterizedTest @ValueSource(booleans = [false, true]) fun setMainClassAttributeFromMainRun(useShadowAttr: Boolean) { - val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin) - val main2ClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin, className = "Main2") val mainClassName = "my.Main" val main2ClassName = "my.Main2" val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" - projectScriptPath.appendText( + projectScript.appendText( """ kotlin { jvm().mainRun { it.mainClass.set('$mainClassName') } } - $shadowJar { + $shadowJarTask { manifest { $mainAttr } @@ -195,13 +193,40 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { - containsAtLeast( - mainClassEntry, - main2ClassEntry, - ) + assertThat(outputShadowedJar).useAll { + getMainAttr(mainClassAttributeKey).isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) + } + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun setManifestAttrsFromJvmTargetJar(useShadowAttr: Boolean) { + val mainClassName = "my.Main" + val main2ClassName = "my.Main2" + val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" + projectScript.appendText( + """ + kotlin { + jvm() + } + tasks.named('jvmJar', Jar) { + manifest { + attributes '$mainClassAttributeKey': '$mainClassName' + } + } + $shadowJarTask { + manifest { + $mainAttr + } + } + """.trimIndent(), + ) + + run(shadowJarPath) + + assertThat(outputShadowedJar).useAll { getMainAttr(mainClassAttributeKey).isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) } } @@ -209,7 +234,7 @@ class KotlinPluginsTest : BasePluginTest() { @Test fun registerShadowJarForFirstJvmTarget() { val jvmTargetName = "newJvm" - projectScriptPath.appendText( + projectScript.appendText( """ kotlin { jvm() // Default JVM target. @@ -235,7 +260,7 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = runWithFailure(shadowJarTask, INFO_ARGUMENT) + val result = runWithFailure(shadowJarPath, INFO_ARGUMENT) assertThat(result.output).contains( "$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: $jvmTargetName", // Logged from Shadow. @@ -243,43 +268,6 @@ class KotlinPluginsTest : BasePluginTest() { ) } - @ParameterizedTest - @ValueSource(booleans = [false, true]) - fun setManifestAttrsFromJvmTargetJar(useShadowAttr: Boolean) { - val mainClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin) - val main2ClassEntry = writeClass(sourceSet = "jvmMain", jvmLang = JvmLang.Kotlin, className = "Main2") - val mainClassName = "my.Main" - val main2ClassName = "my.Main2" - val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" - projectScriptPath.appendText( - """ - kotlin { - jvm() - } - tasks.named('jvmJar', Jar) { - manifest { - attributes '$mainClassAttributeKey': '$mainClassName' - } - } - $shadowJar { - manifest { - $mainAttr - } - } - """.trimIndent(), - ) - - run(shadowJarTask) - - assertThat(outputShadowJar).useAll { - containsAtLeast( - mainClassEntry, - main2ClassEntry, - ) - getMainAttr(mainClassAttributeKey).isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) - } - } - private fun compileOnlyStdlib(exclude: Boolean): String { return if (exclude) { // Disable the stdlib dependency added via `implementation`. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt similarity index 91% rename from src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt rename to src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index 0c3c68736..fbdc57f51 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -class MinimizationTest : BasePluginTest() { +class MinimizeTest : BasePluginTest() { /** * 'api' used as api for 'impl', and depended on 'lib'. 'junit' is independent. * The minimize step shall remove 'junit', but not 'api'. @@ -94,9 +94,9 @@ class MinimizationTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { containsAtLeast( "client/Client.class", "server/Server.class", @@ -122,9 +122,9 @@ class MinimizationTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { containsAtLeast( "server/Server.class", *junitEntries, @@ -149,9 +149,9 @@ class MinimizationTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { containsOnly( "client/", "server/", @@ -186,9 +186,9 @@ class MinimizationTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { containsAtLeast( "client/Client.class", "server/Server.class", @@ -202,9 +202,9 @@ class MinimizationTest : BasePluginTest() { public class Client {} """.trimIndent(), ) - run(serverShadowJarTask) + run(serverShadowJarPath) - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { containsAtLeast( "client/Client.class", "server/Server.class", @@ -219,12 +219,12 @@ class MinimizationTest : BasePluginTest() { writeClientAndServerModules() if (enable) { - run(serverShadowJarTask, "--minimize-jar") + run(serverShadowJarPath, "--minimize-jar") } else { - run(serverShadowJarTask) + run(serverShadowJarPath) } - assertThat(outputServerShadowJar).useAll { + assertThat(outputServerShadowedJar).useAll { if (enable) { containsAtLeast( "server/Server.class", @@ -247,12 +247,12 @@ class MinimizationTest : BasePluginTest() { } private fun writeApiLibAndImplModules() { - settingsScriptPath.appendText( + settingsScript.appendText( """ include 'api', 'lib', 'impl' """.trimIndent() + lineSeparator, ) - projectScriptPath.writeText("") + projectScript.writeText("") path("lib/src/main/java/lib/LibEntity.java").writeText( """ @@ -312,7 +312,7 @@ class MinimizationTest : BasePluginTest() { dependencies { api project(':api') } - $shadowJar { + $shadowJarTask { minimize() } """.trimIndent() + lineSeparator, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index f7daa3fb6..ebeddf3eb 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -54,12 +54,12 @@ class PublishingTest : BasePluginTest() { @BeforeEach override fun setup() { super.setup() - settingsScriptPath.appendText("rootProject.name = 'maven'$lineSeparator") + settingsScript.appendText("rootProject.name = 'maven'$lineSeparator") } @Test fun publishShadowJar() { - projectScriptPath.appendText( + projectScript.appendText( publishConfiguration( shadowBlock = """ archiveClassifier = '' @@ -90,7 +90,7 @@ class PublishingTest : BasePluginTest() { val targetJvmAttr11 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "11" val targetJvmAttr8 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "8" - projectScriptPath.appendText( + projectScript.appendText( """ java { toolchain.languageVersion = JavaLanguageVersion.of(17) @@ -100,7 +100,7 @@ class PublishingTest : BasePluginTest() { publish() assertions(attrsWithoutTargetJvm + targetJvmAttr17) - projectScriptPath.appendText( + projectScript.appendText( """ java { targetCompatibility = JavaVersion.VERSION_11 @@ -110,7 +110,7 @@ class PublishingTest : BasePluginTest() { publish() assertions(attrsWithoutTargetJvm + targetJvmAttr11) - projectScriptPath.appendText( + projectScript.appendText( """ java { sourceCompatibility = JavaVersion.VERSION_1_8 @@ -121,7 +121,7 @@ class PublishingTest : BasePluginTest() { // sourceCompatibility doesn't affect the target JVM version. assertions(attrsWithoutTargetJvm + targetJvmAttr11) - projectScriptPath.appendText( + projectScript.appendText( """ tasks.named('compileJava') { options.release = 8 @@ -135,7 +135,7 @@ class PublishingTest : BasePluginTest() { @Test fun publishShadowJarInsteadOfJar() { - projectScriptPath.appendText( + projectScript.appendText( publishConfiguration( shadowBlock = """ archiveClassifier = '' @@ -157,7 +157,7 @@ class PublishingTest : BasePluginTest() { @Test fun publishCustomShadowJar() { - projectScriptPath.appendText( + projectScript.appendText( publishConfiguration( projectBlock = """ def testShadowJar = tasks.register('testShadowJar', ${ShadowJar::class.java.name}) { @@ -192,7 +192,7 @@ class PublishingTest : BasePluginTest() { @ValueSource(booleans = [false, true]) fun publishShadowedGradlePlugin(legacy: Boolean) { writeGradlePluginModule(legacy) - projectScriptPath.appendText( + projectScript.appendText( publishConfiguration( projectBlock = """ apply plugin: 'com.gradle.plugin-publish' @@ -232,7 +232,7 @@ class PublishingTest : BasePluginTest() { ) @Test fun publishShadowJarWithCustomArtifactName() { - projectScriptPath.appendText( + projectScript.appendText( publishConfiguration( projectBlock = """ group = 'my-group' @@ -279,12 +279,12 @@ class PublishingTest : BasePluginTest() { @Test fun publishMultiProjectShadowJar() { - settingsScriptPath.appendText( + settingsScript.appendText( """ include 'a', 'b', 'c' """.trimIndent(), ) - projectScriptPath.writeText( + projectScript.writeText( """ subprojects { apply plugin: 'java' @@ -381,7 +381,7 @@ class PublishingTest : BasePluginTest() { @Test fun publishShadowJarWithGradleMetadata() { - projectScriptPath.appendText( + projectScript.appendText( publishConfiguration( projectBlock = """ group = 'com.acme' @@ -485,7 +485,7 @@ class PublishingTest : BasePluginTest() { dependencies { $dependenciesBlock } - $shadowJar { + $shadowJarTask { $shadowBlock } ${publishingBlock(projectBlock = projectBlock, publicationsBlock = publicationsBlock)} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 79c7775d6..6a15cfa20 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -29,12 +29,12 @@ class RelocationTest : BasePluginTest() { @MethodSource("prefixProvider") fun autoRelocation(relocationPrefix: String) { val mainClassEntry = writeClass() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } - $shadowJar { + $shadowJarTask { enableAutoRelocation = true relocationPrefix = '$relocationPrefix' } @@ -53,9 +53,9 @@ class RelocationTest : BasePluginTest() { } }.toTypedArray() - val result = run(shadowJarTask, INFO_ARGUMENT) + val result = run(shadowJarPath, INFO_ARGUMENT) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "my/", mainClassEntry, @@ -75,12 +75,12 @@ class RelocationTest : BasePluginTest() { @Test fun relocateDependencyFiles() { val mainClassEntry = writeClass() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } - $shadowJar { + $shadowJarTask { relocate 'junit.runner', 'a' relocate 'junit.framework', 'b' } @@ -96,9 +96,9 @@ class RelocationTest : BasePluginTest() { .map { it.replace("junit/framework/", "b/") }.toTypedArray() val otherJunitEntries = junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "my/", mainClassEntry, @@ -113,12 +113,12 @@ class RelocationTest : BasePluginTest() { @Test fun relocateDependencyFilesWithFiltering() { val mainClassEntry = writeClass() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } - $shadowJar { + $shadowJarTask { relocate('junit.runner', 'a') { exclude 'junit.runner.BaseTestRunner' } @@ -138,9 +138,9 @@ class RelocationTest : BasePluginTest() { .map { it.replace("junit/framework/", "b/") }.toTypedArray() val otherJunitEntries = junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "b/", "my/", @@ -160,12 +160,12 @@ class RelocationTest : BasePluginTest() { ) @Test fun remapClassNamesForRelocatedFilesInProjectSource() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } - $shadowJar { + $shadowJarTask { relocate 'junit.framework', 'shadow.junit' } """.trimIndent(), @@ -185,9 +185,9 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "my/", "shadow/", @@ -197,7 +197,7 @@ class RelocationTest : BasePluginTest() { ) } - val url = outputShadowJar.use { it.toUri().toURL() } + val url = outputShadowedJar.use { it.toUri().toURL() } URLClassLoader(arrayOf(url), ClassLoader.getSystemClassLoader().parent).use { classLoader -> assertFailure { // check that the class can be loaded. If the file was not relocated properly, we should get a NoDefClassFound @@ -210,7 +210,7 @@ class RelocationTest : BasePluginTest() { @Test fun relocateDoesNotDropDependencyResources() { - settingsScriptPath.appendText( + settingsScript.appendText( """ include 'core', 'app' """.trimIndent(), @@ -242,7 +242,7 @@ class RelocationTest : BasePluginTest() { dependencies { implementation project(':core') } - $shadowJar { + $shadowJarTask { relocate 'core', 'app.core' relocate 'junit.framework', 'app.junit.framework' } @@ -297,20 +297,20 @@ class RelocationTest : BasePluginTest() { ) path("src/main/resources/foo/foo.properties").writeText("name=foo") - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:dep:1.0' } - $shadowJar { + $shadowJarTask { relocate 'foo', 'bar' } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "bar/", "bar/Foo.class", @@ -326,7 +326,7 @@ class RelocationTest : BasePluginTest() { ) @Test fun doNotErrorOnRelocatingJava9Classes() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'org.slf4j:slf4j-api:1.7.21' @@ -334,7 +334,7 @@ class RelocationTest : BasePluginTest() { implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '2.5.0' implementation group: 'org.apache.zookeeper', name: 'zookeeper', version: '3.4.6' } - $shadowJar { + $shadowJarTask { zip64 = true relocate 'com.google.protobuf', 'shaded.com.google.protobuf' relocate 'io.netty', 'shaded.io.netty' @@ -342,9 +342,9 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - val entries = outputShadowJar.use { it.entries().toList() } + val entries = outputShadowedJar.use { it.entries().toList() } val included = entries.filter { entry -> entry.name.startsWith("shaded/com/google/protobuf") || entry.name.startsWith("shaded/io/netty") } @@ -362,22 +362,22 @@ class RelocationTest : BasePluginTest() { val currentTimeMillis = System.currentTimeMillis() - 3.seconds.inWholeMilliseconds val junitEntryTimeRange = junitRawEntries.map { it.time }.let { it.min()..it.max() } writeClass(withImports = true) - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } - $shadowJar { + $shadowJarTask { enableAutoRelocation = $enableAutoRelocation preserveFileTimestamps = $preserveFileTimestamps } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) if (enableAutoRelocation) { - val (relocatedEntries, otherEntries) = outputShadowJar.use { + val (relocatedEntries, otherEntries) = outputShadowedJar.use { it.entries().toList().partition { entry -> entry.name.startsWith("shadow/") } } assertThat(relocatedEntries).isNotEmpty() @@ -408,7 +408,7 @@ class RelocationTest : BasePluginTest() { } } } else { - val (shadowedEntries, otherEntries) = outputShadowJar.use { + val (shadowedEntries, otherEntries) = outputShadowedJar.use { it.entries().toList().partition { entry -> entry.name.startsWith("junit/") } } assertThat(shadowedEntries).isNotEmpty() @@ -448,12 +448,12 @@ class RelocationTest : BasePluginTest() { val kotlinJar = buildJar("kotlin.jar") { insert("kotlin/kotlin.kotlin_builtins", "This is a Kotlin builtins file.") } - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(kotlinJar)} } - $shadowJar { + $shadowJarTask { relocate('kotlin.', 'foo.kotlin.') { exclude('kotlin/kotlin.kotlin_builtins') } @@ -461,9 +461,9 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "kotlin/", "kotlin/kotlin.kotlin_builtins", @@ -483,12 +483,12 @@ class RelocationTest : BasePluginTest() { } else { "" } - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } - $shadowJar { + $shadowJarTask { relocate('', 'foo/') { $relocateConfig } @@ -496,9 +496,9 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { if (exclude) { containsOnly( *junitEntries, @@ -517,21 +517,21 @@ class RelocationTest : BasePluginTest() { @Test fun relocateProjectResourcesOnly() { val mainClassEntry = writeClass() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' } - $shadowJar { + $shadowJarTask { configurations = [] relocate('', 'foo/') } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "foo/", "foo/my/", @@ -546,7 +546,7 @@ class RelocationTest : BasePluginTest() { @MethodSource("relocationCliOptionProvider") fun enableAutoRelocationByCliOption(enable: Boolean, relocationPrefix: String) { val mainClassEntry = writeClass() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'junit:junit:3.8.2' @@ -558,12 +558,12 @@ class RelocationTest : BasePluginTest() { .toTypedArray() if (enable) { - run(shadowJarTask, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") + run(shadowJarPath, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") } else { - run(shadowJarTask, "--relocation-prefix=$relocationPrefix") + run(shadowJarPath, "--relocation-prefix=$relocationPrefix") } - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { if (enable) { containsOnly( "my/", @@ -586,9 +586,9 @@ class RelocationTest : BasePluginTest() { @Test fun relocateStringConstantsByDefault() { writeClassWithStringRef() - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { manifest { attributes '$mainClassAttributeKey': 'my.Main' } @@ -597,8 +597,8 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) - val result = runProcess("java", "-jar", outputShadowJar.use { it.toString() }) + run(shadowJarPath) + val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) assertThat(result).contains( "shadow.foo.Foo", @@ -614,9 +614,9 @@ class RelocationTest : BasePluginTest() { @ValueSource(booleans = [false, true]) fun disableRelocateStringConstants(skipStringConstants: Boolean) { writeClassWithStringRef() - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { manifest { attributes '$mainClassAttributeKey': 'my.Main' } @@ -627,8 +627,8 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) - val result = runProcess("java", "-jar", outputShadowJar.use { it.toString() }) + run(shadowJarPath) + val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) if (skipStringConstants) { assertThat(result).contains( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt index a2dca340d..e365bc6bd 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt @@ -17,13 +17,13 @@ class ScalaPluginTest : BasePluginTest() { withGroup = true, withVersion = true, ) - projectScriptPath.writeText(projectBuildScript) + projectScript.writeText(projectBuildScript) } @Test fun compatScala() { val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Scala) - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { compileOnly 'org.scala-lang:scala-library:2.13.16' @@ -32,9 +32,9 @@ class ScalaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "my/", "my/Main$.class", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index bfebbaf43..63bdea05e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -22,7 +22,7 @@ class AppendingTransformerTest : BaseTransformerTest() { dependencies { ${implementationFiles(one, two)} } - $shadowJar { + $shadowJarTask { append('$ENTRY_TEST_PROPERTIES') } """.trimIndent() @@ -34,11 +34,11 @@ class AppendingTransformerTest : BaseTransformerTest() { """.trimIndent(), ) } - projectScriptPath.appendText(config) + projectScript.appendText(config) - run(shadowJarTask) + run(shadowJarPath) - val content = outputShadowJar.use { it.getContent(ENTRY_TEST_PROPERTIES) } + val content = outputShadowedJar.use { it.getContent(ENTRY_TEST_PROPERTIES) } assertThat(content).isEqualTo(CONTENT_ONE_TWO) } @@ -58,7 +58,7 @@ class AppendingTransformerTest : BaseTransformerTest() { dependencies { ${implementationFiles(one, two)} } - $shadowJar { + $shadowJarTask { append('resources/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') append('resources/config/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') } @@ -81,11 +81,11 @@ class AppendingTransformerTest : BaseTransformerTest() { block1 + lineSeparator + block2 } - projectScriptPath.appendText(config) + projectScript.appendText(config) - run(shadowJarTask) + run(shadowJarPath) - val content1 = outputShadowJar.use { it.getContent("resources/$APPLICATION_YML_FILE") } + val content1 = outputShadowedJar.use { it.getContent("resources/$APPLICATION_YML_FILE") } assertThat(content1).isEqualTo( """ $CONTENT_ONE @@ -93,7 +93,7 @@ class AppendingTransformerTest : BaseTransformerTest() { $CONTENT_TWO """.trimIndent(), ) - val content2 = outputShadowJar.use { it.getContent("resources/config/$APPLICATION_YML_FILE") } + val content2 = outputShadowedJar.use { it.getContent("resources/config/$APPLICATION_YML_FILE") } assertThat(content2).isEqualTo( """ $CONTENT_TWO diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index ff4204135..5ed589c12 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -26,7 +26,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { dependencies { ${implementationFiles(buildJarFoo(), buildJarBar())} } - $shadowJar { + $shadowJarTask { mergeGroovyExtensionModules() } """.trimIndent() @@ -35,16 +35,16 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { dependenciesBlock = implementationFiles(buildJarFoo(), buildJarBar()), ) } - projectScriptPath.appendText(config) + projectScript.appendText(config) - run(shadowJarTask) + run(shadowJarPath) commonAssertions(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) } @Test fun groovyExtensionModuleTransformerWorksForLegacyGroovy() { - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles( buildJarFoo(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), @@ -53,7 +53,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarTask) + run(shadowJarPath) commonAssertions(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR) } @@ -87,7 +87,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { } private fun commonAssertions(entry: String) { - val properties = outputShadowJar.use { it.getContent(entry) }.toProperties() + val properties = outputShadowedJar.use { it.getContent(entry) }.toProperties() assertThat(properties.getProperty(KEY_MODULE_NAME)).isEqualTo(MERGED_MODULE_NAME) assertThat(properties.getProperty(KEY_MODULE_VERSION)).isEqualTo(MERGED_MODULE_VERSION) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index 800e5818e..f7057d3a8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -22,7 +22,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { val two = buildJarTwo { insert("META-INF/test.properties", "key2=two\nkey3=two") } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = """ @@ -33,14 +33,14 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarTask) + run(shadowJarPath) val expected = when (strategy) { MergeStrategy.First -> arrayOf("key1=one", "key2=one", "key3=two") MergeStrategy.Latest -> arrayOf("key1=one", "key2=two", "key3=two") MergeStrategy.Append -> arrayOf("key1=one", "key2=one;two", "key3=two") } - val content = outputShadowJar.use { it.getContent("META-INF/test.properties") } + val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } assertThat(content).contains(*expected) } @@ -52,7 +52,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { val two = buildJarTwo { insert("META-INF/test.properties", "FOO=baz") } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = """ @@ -63,9 +63,9 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarTask) + run(shadowJarPath) - val content = outputShadowJar.use { it.getContent("META-INF/test.properties") } + val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } assertThat(content).contains("FOO=bar,baz") } @@ -77,7 +77,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { val two = buildJarTwo { insert("META-INF/utf8.properties", "foo=第二") } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = """ @@ -88,9 +88,9 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarTask) + run(shadowJarPath) - val content = outputShadowJar.use { it.getContent("META-INF/utf8.properties") } + val content = outputShadowedJar.use { it.getContent("META-INF/utf8.properties") } assertThat(content).contains("foo=第一,第二") } @@ -104,7 +104,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { insert("META-INF/foo.properties", "foo=3") insert("META-INF/bar.properties", "bar=4") } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = """ @@ -116,9 +116,9 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { getContent("META-INF/foo.properties").contains("foo=1;3") getContent("META-INF/bar.properties").contains("bar=4") } @@ -136,7 +136,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { val two = buildJarTwo { insert("META-INF/test.properties", "foo=two") } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = """ @@ -146,9 +146,9 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarTask) + run(shadowJarPath) - val content = outputShadowJar.use { it.getContent("META-INF/test.properties") } + val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } assertThat(content.trimIndent()).isEqualTo( """ # diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index d5f597c00..664f883d8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -23,7 +23,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { dependencies { ${implementationFiles(buildJarOne(), buildJarTwo())} } - $shadowJar { + $shadowJarTask { mergeServiceFiles { exclude 'META-INF/services/com.acme.*' } @@ -37,11 +37,11 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """.trimIndent(), ) } - projectScriptPath.appendText(config) + projectScript.appendText(config) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) getContent(ENTRY_SERVICES_FOO).isEqualTo("two") } @@ -61,7 +61,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { dependencies { ${implementationFiles(one, two)} } - $shadowJar { + $shadowJarTask { mergeServiceFiles("META-INF/foo") } """.trimIndent() @@ -73,11 +73,11 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """.trimIndent(), ) } - projectScriptPath.appendText(config) + projectScript.appendText(config) - run(shadowJarTask) + run(shadowJarPath) - val content = outputShadowJar.use { it.getContent(ENTRY_FOO_SHADE) } + val content = outputShadowedJar.use { it.getContent(ENTRY_FOO_SHADE) } assertThat(content).isEqualTo(CONTENT_ONE_TWO) } @@ -118,12 +118,12 @@ class ServiceFileTransformerTest : BaseTransformerTest() { ) } - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(one, two)} } - $shadowJar { + $shadowJarTask { mergeServiceFiles() relocate("org.apache", "myapache") { exclude 'org.apache.axis.components.compiler.Jikes' @@ -133,9 +133,9 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { getContent("META-INF/services/java.sql.Driver").isEqualTo( """ oracle.jdbc.OracleDriver @@ -175,22 +175,22 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } }.publish() - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'foo:bar:1.0' ${implementationFiles(one)} } - $shadowJar { + $shadowJarTask { mergeServiceFiles() } """.trimIndent(), ) path("src/main/resources/$servicesBarEntry").writeText(CONTENT_THREE) - run(shadowJarTask) + run(shadowJarPath) - val content = outputShadowJar.use { it.getContent(servicesBarEntry) } + val content = outputShadowedJar.use { it.getContent(servicesBarEntry) } assertThat(content).isEqualTo(CONTENT_THREE + "\n" + CONTENT_ONE_TWO) } @@ -202,7 +202,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { ) { writeDuplicatesStrategy(strategy) - val result = runWithFailure(shadowJarTask) + val result = runWithFailure(shadowJarPath) assertThat(result.output).containsMatch(outputRegex.toRegex()) } @@ -216,9 +216,9 @@ class ServiceFileTransformerTest : BaseTransformerTest() { ) { writeDuplicatesStrategy(strategy) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(firstValue) getContent(ENTRY_SERVICES_FOO).isEqualTo(secondValue) } @@ -227,9 +227,9 @@ class ServiceFileTransformerTest : BaseTransformerTest() { @Test fun strategyExcludeCanBeOverriddenByFilesMatching() { writeDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) - projectScriptPath.appendText( + projectScript.appendText( """ - $shadowJar { + $shadowJarTask { filesMatching('$ENTRY_SERVICES_SHADE') { duplicatesStrategy = DuplicatesStrategy.INCLUDE } @@ -237,21 +237,21 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) getContent(ENTRY_SERVICES_FOO).isEqualTo("one") } } private fun writeDuplicatesStrategy(strategy: DuplicatesStrategy) { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { ${implementationFiles(buildJarOne(), buildJarTwo())} } - $shadowJar { + $shadowJarTask { duplicatesStrategy = DuplicatesStrategy.$strategy mergeServiceFiles() } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 6dd527637..b79aef879 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -27,9 +27,9 @@ class TransformersTest : BaseTransformerTest() { @Test fun manifestRetained() { writeClass() - projectScriptPath.appendText( + projectScript.appendText( """ - jar { + $jarTask { manifest { attributes '$mainClassAttributeKey': 'my.Main' attributes '$TEST_ENTRY_ATTR_KEY': 'PASSED' @@ -38,7 +38,7 @@ class TransformersTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) commonAssertions { assertThat(getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("PASSED") @@ -50,9 +50,9 @@ class TransformersTest : BaseTransformerTest() { fun manifestTransformed() { writeClass() - projectScriptPath.appendText(MANIFEST_ATTRS) + projectScript.appendText(MANIFEST_ATTRS) - run(shadowJarTask) + run(shadowJarPath) commonAssertions() } @@ -63,9 +63,9 @@ class TransformersTest : BaseTransformerTest() { @Test fun shadowManifestLeaksToJarManifest() { writeClass() - projectScriptPath.appendText(MANIFEST_ATTRS) + projectScript.appendText(MANIFEST_ATTRS) - run("jar", shadowJarTask) + run("jar", shadowJarPath) commonAssertions() @@ -88,15 +88,15 @@ class TransformersTest : BaseTransformerTest() { val two = buildJarOne { insert(PLUGIN_CACHE_FILE, content) } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), ), ) - run(shadowJarTask) + run(shadowJarPath) - val actualFileBytes = outputShadowJar.use { jar -> + val actualFileBytes = outputShadowedJar.use { jar -> jar.getStream(PLUGIN_CACHE_FILE).use { it.readAllBytes() } } assertThat(actualFileBytes.contentHashCode()).all { @@ -109,7 +109,7 @@ class TransformersTest : BaseTransformerTest() { @Test fun includeResource() { val foo = path("foo").apply { writeText("foo") } - projectScriptPath.appendText( + projectScript.appendText( transform( transformerBlock = """ resource = 'bar' @@ -118,9 +118,9 @@ class TransformersTest : BaseTransformerTest() { ), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "bar", *manifestEntries, @@ -135,16 +135,16 @@ class TransformersTest : BaseTransformerTest() { insert("foo", "bar") insert("bar", "foo") } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one), transformerBlock = "resource = 'foo'", ), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "bar", *manifestEntries, @@ -165,16 +165,16 @@ class TransformersTest : BaseTransformerTest() { insert("foo/bar", "bar3") insert("foo/baz", "baz3") } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = "resources = ['foo/bar']", ), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( "foo/", "foo/bar", @@ -188,22 +188,22 @@ class TransformersTest : BaseTransformerTest() { @Test fun useCustomTransformer() { - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:a:1.0' implementation 'my:b:1.0' } - $shadowJar { + $shadowJarTask { // Use Transformer.Companion (no-op) to mock a custom transformer here. transform(${ResourceTransformer.Companion::class.java.name}) } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsOnly( *entriesInAB, *manifestEntries, @@ -215,21 +215,21 @@ class TransformersTest : BaseTransformerTest() { @MethodSource("transformerConfigProvider") fun otherTransformers(pair: Pair>) { val (configuration, transformer) = pair - projectScriptPath.appendText( + projectScript.appendText( """ dependencies { implementation 'my:a:1.0' implementation 'my:b:1.0' } - $shadowJar { + $shadowJarTask { transform(${transformer.java.name}) $configuration } """.trimIndent(), ) - run(shadowJarTask) + run(shadowJarPath) - assertThat(outputShadowJar).useAll { + assertThat(outputShadowedJar).useAll { containsAtLeast(*entriesInAB) } } @@ -241,7 +241,7 @@ class TransformersTest : BaseTransformerTest() { assertThat(getValue(NEW_ENTRY_ATTR_KEY)).isEqualTo("NEW") }, ) { - val mf = outputShadowJar.use { it.manifest } + val mf = outputShadowedJar.use { it.manifest } assertThat(mf).isNotNull() mainAttributesBlock(mf.mainAttributes) } @@ -251,13 +251,13 @@ class TransformersTest : BaseTransformerTest() { const val TEST_ENTRY_ATTR_KEY = "Test-Entry" val MANIFEST_ATTRS = """ - jar { + $jarTask { manifest { attributes '$mainClassAttributeKey': 'my.Main' attributes '$TEST_ENTRY_ATTR_KEY': 'FAILED' } } - $shadowJar { + $shadowJarTask { manifest { attributes '$NEW_ENTRY_ATTR_KEY': 'NEW' attributes '$TEST_ENTRY_ATTR_KEY': 'PASSED' diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index 8bcdcfad7..d35ae4d48 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -25,7 +25,7 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { insert(xmlEntry, xmlContent.format("key2", "val2")) } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = """ @@ -34,9 +34,9 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarTask) + run(shadowJarPath) - val content = outputShadowJar.use { it.getContent(xmlEntry) }.trimIndent() + val content = outputShadowedJar.use { it.getContent(xmlEntry) }.trimIndent() assertThat(content).isEqualTo( """ @@ -66,7 +66,7 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { insert(xmlEntry, xmlContent.format("")) } - projectScriptPath.appendText( + projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = """ @@ -75,9 +75,9 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarTask) + run(shadowJarPath) - val content = outputShadowJar.use { it.getContent(xmlEntry) }.trimIndent() + val content = outputShadowedJar.use { it.getContent(xmlEntry) }.trimIndent() assertThat(content).isEqualTo( """ From b3dd248fcae3ea38dac61cac5e86c0aff203fad0 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 8 Aug 2025 11:34:12 +0800 Subject: [PATCH 632/941] Update changelog --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ee079a86f..82e51b303 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,7 +3,7 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0...HEAD) - 2025-xx-xx -### Added +### Changed - Improve the error message for empty `mainClassName`. ([#1601](https://github.com/GradleUp/shadow/pull/1601)) From 8ca103c2650e8e6a017b27fd24387b244f7b0898 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 8 Aug 2025 11:42:16 +0800 Subject: [PATCH 633/941] Fix the regression of can't shadow directory inputs (#1613) * Call `from(archiveOperations.zipTree(file))` * Check AAR files are regular files * Test `shadowExposedCustomSourceSetOutput` * Add dirs directly without unzipping * Update changelog * Use `when` * Use `forEach` --- docs/changes/README.md | 4 ++ .../gradle/plugins/shadow/JavaPluginsTest.kt | 47 +++++++++++++++++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 17 ++++--- 3 files changed, 62 insertions(+), 6 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 82e51b303..22d6d9d5a 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,6 +7,10 @@ - Improve the error message for empty `mainClassName`. ([#1601](https://github.com/GradleUp/shadow/pull/1601)) +### Fixed + +- Fix the regression of can't shadow directory inputs. ([#1606](https://github.com/GradleUp/shadow/pull/1606)) + ## [9.0.0](https://github.com/GradleUp/shadow/compare/9.0.0) - 2025-08-07 !!! warning diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index d2c17cac5..91593960c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -191,6 +191,53 @@ class JavaPluginsTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/1606", + ) + @Test + fun shadowExposedCustomSourceSetOutput() { + writeClientAndServerModules() + path("client/build.gradle").appendText( + """ + sourceSets { + custom + } + dependencies { + implementation sourceSets.custom.output + } + """.trimIndent(), + ) + path("client/src/custom/java/client/Custom1.java").writeText( + """ + package client; + public class Custom1 {} + """.trimIndent(), + ) + path("client/src/custom/java/client/Custom2.java").writeText( + """ + package client; + public class Custom2 {} + """.trimIndent(), + ) + path("client/src/custom/resources/Foo.bar").writeText("Foo=Bar") + + run(serverShadowJarPath) + + assertThat(outputServerShadowedJar).useAll { + containsOnly( + "Foo.bar", + "client/", + "server/", + "client/Client.class", + "client/Custom1.class", + "client/Custom2.class", + "server/Server.class", + *junitEntries, + *manifestEntries, + ) + } + } + @Issue( "https://github.com/GradleUp/shadow/issues/449", ) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 386a08e9f..bff26b860 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -345,9 +345,12 @@ public abstract class ShadowJar : Jar() { @TaskAction override fun copy() { - from( - includedDependencies.files.map { file -> - if (file.extension.equals("aar", ignoreCase = true)) { + includedDependencies.files.forEach { file -> + when { + file.isDirectory -> { + from(file) + } + file.extension.equals("aar", ignoreCase = true) -> { val message = """ Shadowing AAR file is not supported. Please exclude dependency artifact: $file @@ -355,9 +358,11 @@ public abstract class ShadowJar : Jar() { """.trimIndent() error(message) } - archiveOperations.zipTree(file) - }, - ) + else -> { + from(archiveOperations.zipTree(file)) + } + } + } injectMultiReleaseAttrIfPresent() super.copy() } From 94109aa55e2ab158cf623158feb3c2bcaaa595ae Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 8 Aug 2025 14:08:44 +0800 Subject: [PATCH 634/941] Try out windows-11-arm runners (#1614) --- .github/workflows/build.yml | 5 ++++- .../gradle/plugins/shadow/ApplicationPluginTest.kt | 7 +++++++ .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 7 +++++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ec1c988da..c1d2d0def 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,10 @@ jobs: java: 24 include: # We just test one JDK version on Windows. - - os: windows-latest + - os: windows-11-arm + gradle: 8.11 + java: 21 + - os: windows-11-arm gradle: current java: 21 runs-on: ${{ matrix.os }} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index c86527879..494518998 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -27,11 +27,18 @@ import kotlin.io.path.relativeTo import kotlin.io.path.walk import kotlin.io.path.writeText import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.DisabledOnOs +import org.junit.jupiter.api.condition.OS @ExperimentalPathApi class ApplicationPluginTest : BasePluginTest() { private lateinit var mainClass: String + @DisabledOnOs( + OS.WINDOWS, + architectures = ["aarch64"], + disabledReason = "https://github.com/gradle/gradle/issues/29807", + ) @Test fun integrationWithApplicationPluginAndJavaToolchains() { prepare( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index ebeddf3eb..aae5f7f50 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -43,6 +43,8 @@ import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME import org.gradle.testkit.runner.BuildResult import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test +import org.junit.jupiter.api.condition.DisabledOnOs +import org.junit.jupiter.api.condition.OS import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource @@ -57,6 +59,11 @@ class PublishingTest : BasePluginTest() { settingsScript.appendText("rootProject.name = 'maven'$lineSeparator") } + @DisabledOnOs( + OS.WINDOWS, + architectures = ["aarch64"], + disabledReason = "https://github.com/gradle/gradle/issues/29807", + ) @Test fun publishShadowJar() { projectScript.appendText( From f10211f0d3e0b3f9227b727673086f6fe787f7df Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 8 Aug 2025 14:27:24 +0800 Subject: [PATCH 635/941] Revert "Disable lint tasks on Windows (#1526)" This reverts commit edcb14397dd74550646670d0c15ff94ca1240131. --- build.gradle.kts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index f02d6ecbf..d3da61347 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,7 @@ import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME -import org.jetbrains.kotlin.daemon.common.OSKind +import org.gradle.kotlin.dsl.kotlin import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion @@ -239,13 +239,6 @@ tasks.validatePlugins { enableStricterValidation = true } -tasks.whenTaskAdded { - if (name.contains("lint") && this::class.java.name.contains("com.android.build")) { - // Disable lint tasks for Windows due to ExceptionInInitializerError. - enabled = OSKind.current != OSKind.Windows - } -} - tasks.check { dependsOn( tasks.withType(), From 462d7e199e2a6672ee02501c6f17b7d40cf60248 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 8 Aug 2025 15:34:55 +0800 Subject: [PATCH 636/941] Update build.gradle.kts --- build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index d3da61347..a1b456919 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,6 @@ import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.SOURCES_ELEMENTS_CONFIGURATION_NAME -import org.gradle.kotlin.dsl.kotlin import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion From a28ab97c34b08a6a009c7e6811af077aca40c898 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 8 Aug 2025 17:44:46 +0800 Subject: [PATCH 637/941] Improve readability of DefaultDependencyFilter (#1616) --- .../gradle/plugins/shadow/internal/DefaultDependencyFilter.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt index 309e27081..0333c869a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt @@ -13,7 +13,8 @@ internal class DefaultDependencyFilter( excludedDependencies: MutableSet, ) { dependencies.forEach { - if (if (it.isIncluded()) includedDependencies.add(it) else excludedDependencies.add(it)) { + val added = if (it.isIncluded()) includedDependencies.add(it) else excludedDependencies.add(it) + if (added) { resolve(it.children, includedDependencies, excludedDependencies) } } From 936c82aa44a2a2dd68e8a9c98e07b6c12cdfe553 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 9 Aug 2025 10:01:34 +0800 Subject: [PATCH 638/941] Fix typo --- .../github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index bff26b860..378fed602 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -176,7 +176,7 @@ public abstract class ShadowJar : Jar() { * This is related to setting [getDuplicatesStrategy] to [FAIL] but there are some differences: * - It only checks the entries in the shadowed jar, not the input files. * - It works with setting [getDuplicatesStrategy] to any value. - * - It provides a more strict check before the JAR is created. + * - It provides a stricter check before the JAR is created. * * Defaults to `false`. */ From 67dd06fcca56a0f93c12b8f9612ee1ce2af58d87 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 9 Aug 2025 09:59:56 +0800 Subject: [PATCH 639/941] Update docs about DuplicatesStrategy.INCLUDE --- docs/configuration/merging/README.md | 4 ++-- .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 9 ++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 63b82dff7..5061fd997 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -397,10 +397,10 @@ Different strategies will lead to different results for `foo/bar` files in the J - `EXCLUDE`: The **first** `foo/bar` file will be included in the final JAR. - `FAIL`: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. -- `INCLUDE`: The **last** `foo/bar` file will be included in the final JAR (the default behavior). +- `INCLUDE`: Duplicate `foo/bar` entries will be included in the final JAR. - `INHERIT`: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. -- `WARN`: The **last** `foo/bar` file will be included in the final JAR, and a warning message will be logged. +- `WARN`: **Warn** about duplicates in the build log, this behaves exactly as `INHERIT` otherwise. **NOTE:** The `duplicatesStrategy` takes precedence over transforming and relocating. If you mix the usages of `duplicatesStrategy` and [`ResourceTransformer`][ResourceTransformer] like below: diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 378fed602..d237e5c90 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -196,16 +196,15 @@ public abstract class ShadowJar : Jar() { /** * Returns the strategy to use when trying to copy more than one file to the same destination. * - * This strategy can be overridden for individual files by using [filesMatching]. + * This global strategy can be overridden for individual files by using [filesMatching]. * - * The default value is [INCLUDE]. Different strategies will lead to different results for - * `foo/bar` files in the JARs to be merged: + * The default value is [INCLUDE]. Different strategies will lead to different results for `foo/bar` files in the JARs to be merged: * * - [EXCLUDE]: The **first** `foo/bar` file will be included in the final JAR. * - [FAIL]: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. - * - [INCLUDE]: The **last** `foo/bar` file will be included in the final JAR (the default behavior). + * - [INCLUDE]: Duplicate `foo/bar` entries will be included in the final JAR. * - [INHERIT]: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. - * - [WARN]: The **last** `foo/bar` file will be included in the final JAR, and a warning message will be logged. + * - [WARN]: **Warn** about duplicates in the build log, this behaves exactly as [INHERIT] otherwise. * * **NOTE:** The strategy takes precedence over transforming and relocating. * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to From 46d7be10024439f1c770986e8a35447856737ca5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 9 Aug 2025 10:25:20 +0800 Subject: [PATCH 640/941] Pin the handling DuplicatesStrategy section (#1619) --- docs/configuration/merging/README.md | 151 ++++++++++++++------------- 1 file changed, 76 insertions(+), 75 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 5061fd997..3a065c69d 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -14,6 +14,82 @@ should process a particular entry and apply any modifications before writing the This ordering is crucial when merging configuration files where you want to preserve project-specific values while merging in additional data from dependencies. +## Handling Duplicates Strategy + +`ShadowJar` is a subclass of [`org.gradle.api.tasks.AbstractCopyTask`][AbstractCopyTask], which means it honors the +`duplicatesStrategy` property as its parent classes do. There are several strategies to handle: + +- `EXCLUDE`: Do not allow duplicates by ignoring subsequent items to be created at the same path. +- `FAIL`: Throw a `DuplicateFileCopyingException` when subsequent items are to be created at the same path. +- `INCLUDE`: Do not attempt to prevent duplicates. +- `INHERIT`: Uses the same strategy as the parent copy specification. +- `WARN`: Do not attempt to prevent duplicates, but log a warning message when multiple items are to be created at the + same path. + +see more details about them in [`DuplicatesStrategy`][DuplicatesStrategy]. + +`ShadowJar` recognizes `DuplicatesStrategy.INCLUDE` as the default, if you want to change the strategy, you can +override it like: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or something else. + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or something else. + } + ``` + +Different strategies will lead to different results for `foo/bar` files in the JARs to be merged: + +- `EXCLUDE`: The **first** `foo/bar` file will be included in the final JAR. +- `FAIL`: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. +- `INCLUDE`: Duplicate `foo/bar` entries will be included in the final JAR. +- `INHERIT`: **Fail** the build with an exception like + `Entry .* is a duplicate but no duplicate handling strategy has been set`. +- `WARN`: **Warn** about duplicates in the build log, this behaves exactly as `INHERIT` otherwise. + +**NOTE:** The `duplicatesStrategy` takes precedence over transforming and relocating. If you mix the usages of +`duplicatesStrategy` and [`ResourceTransformer`][ResourceTransformer] like below: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + mergeServiceFiles() + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + mergeServiceFiles() + } + ``` + +The [`ResourceTransformer`][ResourceTransformer]s like [`ServiceFileTransformer`][ServiceFileTransformer] will not work +as expected because the `duplicatesStrategy` will exclude the duplicate service files beforehand. However, this behavior might be what you expected for duplicate +`foo/bar` files, preventing them from being included. + +Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several ways to +do it: + +- Use [`filesMatching`][Jar.filesMatching] to override the strategy for specific files. +- Keep `duplicatesStrategy = INCLUDE` and write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates. + +If you just want to keep the current behavior and preserve the first found resources, there is a simple built-in one +called [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer]. + ## Basic ResourceTransformer Usage For simpler use cases, you can create a basic transformer: @@ -360,81 +436,6 @@ It must be added using the [`transform`][ShadowJar.transform] methods. } ``` -## Handling Duplicates Strategy - -`ShadowJar` is a subclass of [`org.gradle.api.tasks.AbstractCopyTask`][AbstractCopyTask], which means it honors the -`duplicatesStrategy` property as its parent classes do. There are several strategies to handle: - -- `EXCLUDE`: Do not allow duplicates by ignoring subsequent items to be created at the same path. -- `FAIL`: Throw a `DuplicateFileCopyingException` when subsequent items are to be created at the same path. -- `INCLUDE`: Do not attempt to prevent duplicates. -- `INHERIT`: Uses the same strategy as the parent copy specification. -- `WARN`: Do not attempt to prevent duplicates, but log a warning message when multiple items are to be created at the - same path. - -see more details about them in [`DuplicatesStrategy`][DuplicatesStrategy]. - -`ShadowJar` recognizes `DuplicatesStrategy.INCLUDE` as the default, if you want to change the strategy, you can -override it like: - -=== "Kotlin" - - ```kotlin - tasks.shadowJar { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or something else. - } - ``` - -=== "Groovy" - - ```groovy - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or something else. - } - ``` - -Different strategies will lead to different results for `foo/bar` files in the JARs to be merged: - -- `EXCLUDE`: The **first** `foo/bar` file will be included in the final JAR. -- `FAIL`: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. -- `INCLUDE`: Duplicate `foo/bar` entries will be included in the final JAR. -- `INHERIT`: **Fail** the build with an exception like - `Entry .* is a duplicate but no duplicate handling strategy has been set`. -- `WARN`: **Warn** about duplicates in the build log, this behaves exactly as `INHERIT` otherwise. - -**NOTE:** The `duplicatesStrategy` takes precedence over transforming and relocating. If you mix the usages of -`duplicatesStrategy` and [`ResourceTransformer`][ResourceTransformer] like below: - -=== "Kotlin" - - ```kotlin - tasks.shadowJar { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - mergeServiceFiles() - } - ``` - -=== "Groovy" - - ```groovy - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - mergeServiceFiles() - } - ``` - -The [`ResourceTransformer`][ResourceTransformer]s like [`ServiceFileTransformer`][ServiceFileTransformer] will not work -as expected because the `duplicatesStrategy` will exclude the duplicate service files beforehand. However, this behavior might be what you expected for duplicate -`foo/bar` files, preventing them from being included. - -Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several ways to -do it: - -- Use [`filesMatching`][Jar.filesMatching] to override the strategy for specific files. -- Keep `duplicatesStrategy = INCLUDE` and write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates. - -If you just want to keep the current behavior and preserve the first found resources, there is a simple built-in one -called [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer]. [AbstractCopyTask]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html From c3c109be3b5fad02d7c0c27034401be26f78b7cf Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 9 Aug 2025 11:12:05 +0800 Subject: [PATCH 641/941] Default duplicatesStrategy back to EXCLUDE This strategy is consistent with 8.x series behavior, which is more compatible for most users upgrading. --- docs/changes/README.md | 4 +++ docs/configuration/merging/README.md | 31 ++++++++++--------- .../gradle/plugins/shadow/JavaPluginsTest.kt | 7 +++-- .../transformers/BaseTransformerTest.kt | 15 +++++++++ .../ServiceFileTransformerTest.kt | 1 + .../gradle/plugins/shadow/tasks/ShadowJar.kt | 25 ++++++++------- 6 files changed, 55 insertions(+), 28 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 22d6d9d5a..480238770 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -6,6 +6,10 @@ ### Changed - Improve the error message for empty `mainClassName`. ([#1601](https://github.com/GradleUp/shadow/pull/1601)) +- Default `duplicatesStrategy` back to `EXCLUDE`. ([#1617](https://github.com/GradleUp/shadow/pull/1617)) + - This strategy is consistent with 8.x series behavior, which is more compatible for most users upgrading. + - For most `ResourceTransformer` users, you need to override the strategy to `INCLUDE` to make them work. + - See more details about the strategies at [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy). ### Fixed diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 3a065c69d..0341aaec5 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -22,20 +22,19 @@ merging in additional data from dependencies. - `EXCLUDE`: Do not allow duplicates by ignoring subsequent items to be created at the same path. - `FAIL`: Throw a `DuplicateFileCopyingException` when subsequent items are to be created at the same path. - `INCLUDE`: Do not attempt to prevent duplicates. -- `INHERIT`: Uses the same strategy as the parent copy specification. +- `INHERIT`: Use the same strategy as the parent copy specification. - `WARN`: Do not attempt to prevent duplicates, but log a warning message when multiple items are to be created at the same path. see more details about them in [`DuplicatesStrategy`][DuplicatesStrategy]. -`ShadowJar` recognizes `DuplicatesStrategy.INCLUDE` as the default, if you want to change the strategy, you can -override it like: +`ShadowJar` recognizes `EXCLUDE` as the default, if you want to change the strategy, you can override it like: === "Kotlin" ```kotlin tasks.shadowJar { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or something else. + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or something else. } ``` @@ -43,7 +42,7 @@ override it like: ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or something else. + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or something else. } ``` @@ -51,7 +50,7 @@ Different strategies will lead to different results for `foo/bar` files in the J - `EXCLUDE`: The **first** `foo/bar` file will be included in the final JAR. - `FAIL`: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. -- `INCLUDE`: Duplicate `foo/bar` entries will be included in the final JAR. +- `INCLUDE`: **Duplicate** `foo/bar` entries will be included in the final JAR. - `INHERIT`: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. - `WARN`: **Warn** about duplicates in the build log, this behaves exactly as `INHERIT` otherwise. @@ -78,17 +77,18 @@ Different strategies will lead to different results for `foo/bar` files in the J ``` The [`ResourceTransformer`][ResourceTransformer]s like [`ServiceFileTransformer`][ServiceFileTransformer] will not work -as expected because the `duplicatesStrategy` will exclude the duplicate service files beforehand. However, this behavior might be what you expected for duplicate -`foo/bar` files, preventing them from being included. +as expected as the duplicate resource files fed for them are excluded beforehand. However, this behavior might be what you expected for duplicate `foo/bar` files, preventing them from being included. -Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several ways to -do it: +Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several steps +to take: -- Use [`filesMatching`][Jar.filesMatching] to override the strategy for specific files. -- Keep `duplicatesStrategy = INCLUDE` and write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates. - -If you just want to keep the current behavior and preserve the first found resources, there is a simple built-in one -called [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer]. +1. Set the strategy to `INCLUDE` or `WARN`. +2. Apply your [`ResourceTransformer`][ResourceTransformer]s. +3. Remove duplicate entries by + - overriding the default strategy for specific files using [`filesMatching`][Jar.filesMatching] + - applying [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer] + - or something similar. +4. Optionally, enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. ## Basic ResourceTransformer Usage @@ -449,6 +449,7 @@ It must be added using the [`transform`][ShadowJar.transform] methods. [ServiceFileTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html [PreserveFirstFoundResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-preserve-first-found-resource-transformer/index.html [ShadowJar.append]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html +[ShadowJar.failOnDuplicateEntries]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/fail-on-duplicate-entries.html [ShadowJar.from]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action) [ShadowJar.transform]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html [ShadowJar]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 91593960c..d27f618e1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -71,7 +71,7 @@ class JavaPluginsTest : BasePluginTest() { // Check extended properties. with(shadowTask as Jar) { - assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.INCLUDE) + assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.EXCLUDE) assertThat(archiveAppendix.orNull).isNull() assertThat(archiveBaseName.get()).isEqualTo(projectName) assertThat(archiveClassifier.get()).isEqualTo("all") @@ -825,7 +825,6 @@ class JavaPluginsTest : BasePluginTest() { ${implementationFiles(fooJar)} } $shadowJarTask { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE excludes.remove( 'module-info.class' ) @@ -929,6 +928,7 @@ class JavaPluginsTest : BasePluginTest() { ${implementationFiles(artifactAJar)} } $shadowJarTask { + duplicatesStrategy = DuplicatesStrategy.INCLUDE failOnDuplicateEntries = $enable } """.trimIndent(), @@ -955,6 +955,9 @@ class JavaPluginsTest : BasePluginTest() { dependencies { ${implementationFiles(artifactAJar)} } + $shadowJarTask { + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } """.trimIndent(), ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index ea4a545ed..69202dd34 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -3,8 +3,23 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.BasePluginTest import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder import java.nio.file.Path +import kotlin.io.path.appendText +import org.junit.jupiter.api.BeforeEach abstract class BaseTransformerTest : BasePluginTest() { + @BeforeEach + override fun setup() { + super.setup() + projectScript.appendText( + """ + $shadowJarTask { + // Most transformers in tests require this to handle duplicate resources. + duplicatesStrategy = DuplicatesStrategy.INCLUDE + } + """.trimIndent() + lineSeparator, + ) + } + fun buildJarOne( builder: JarBuilder.() -> Unit = { insert(ENTRY_SERVICES_SHADE, CONTENT_ONE) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 664f883d8..5992f4d83 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -230,6 +230,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { projectScript.appendText( """ $shadowJarTask { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE filesMatching('$ENTRY_SERVICES_SHADE') { duplicatesStrategy = DuplicatesStrategy.INCLUDE } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index d237e5c90..301eab44b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -71,7 +71,7 @@ public abstract class ShadowJar : Jar() { description = "Create a combined JAR of project and runtime dependencies" // https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L55-L64 - duplicatesStrategy = INCLUDE + duplicatesStrategy = EXCLUDE manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { @@ -176,6 +176,7 @@ public abstract class ShadowJar : Jar() { * This is related to setting [getDuplicatesStrategy] to [FAIL] but there are some differences: * - It only checks the entries in the shadowed jar, not the input files. * - It works with setting [getDuplicatesStrategy] to any value. + * - Usually used with setting [getDuplicatesStrategy] to [INCLUDE] or [WARN]. * - It provides a stricter check before the JAR is created. * * Defaults to `false`. @@ -198,24 +199,26 @@ public abstract class ShadowJar : Jar() { * * This global strategy can be overridden for individual files by using [filesMatching]. * - * The default value is [INCLUDE]. Different strategies will lead to different results for `foo/bar` files in the JARs to be merged: + * The default value is [EXCLUDE]. Different strategies will lead to different results for `foo/bar` files in the JARs to be merged: * * - [EXCLUDE]: The **first** `foo/bar` file will be included in the final JAR. * - [FAIL]: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. - * - [INCLUDE]: Duplicate `foo/bar` entries will be included in the final JAR. + * - [INCLUDE]: **Duplicate** `foo/bar` entries will be included in the final JAR. * - [INHERIT]: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. * - [WARN]: **Warn** about duplicates in the build log, this behaves exactly as [INHERIT] otherwise. * * **NOTE:** The strategy takes precedence over transforming and relocating. - * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to - * [EXCLUDE], as the service files are excluded beforehand. Want [ResourceTransformer]s and the strategy to work - * together? There are several ways to do it: + * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to [EXCLUDE], + * as the duplicate resource files fed for them are excluded beforehand. + * Want [ResourceTransformer]s and the strategy to work together? There are several steps to take: * - * - Use [filesMatching] to override the strategy for specific files. - * - Keep `duplicatesStrategy = INCLUDE` and write your own [ResourceTransformer] to handle duplicates. - * - * If you just want to keep the current behavior and preserve the first found resources, there is a simple built-in one - * called [PreserveFirstFoundResourceTransformer]. + * 1. Set the strategy to [INCLUDE] or [WARN]. + * 2. Apply your [ResourceTransformer]s. + * 3. Remove duplicate entries by + * - overriding the default strategy for specific files using [filesMatching] + * - applying [PreserveFirstFoundResourceTransformer] + * - or something similar. + * 4. Optionally, enable [failOnDuplicateEntries] to check duplicate entries in the final JAR. * * @see [filesMatching] * @see [DuplicatesStrategy] From ed11c00ef5b2220807a0e2e7f8cf103b629d9451 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 9 Aug 2025 16:58:14 +0800 Subject: [PATCH 642/941] Fix the regression of resolving circular dependencies in MinimizeDependencyFilter (#1611) Refs https://github.com/GradleUp/shadow/blob/3d8fd5171c014ec40fc908c1d5109d10e386d3f3/src/main/groovy/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.groovy#L20-L22 --- docs/changes/README.md | 1 + .../gradle/plugins/shadow/MinimizeTest.kt | 33 +++++++++++++++++++ .../internal/MinimizeDependencyFilter.kt | 6 ++-- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 480238770..ffdbb8106 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -14,6 +14,7 @@ ### Fixed - Fix the regression of can't shadow directory inputs. ([#1606](https://github.com/GradleUp/shadow/pull/1606)) +- Fix the regression of `MinimizeDependencyFilter`. ([#1611](https://github.com/GradleUp/shadow/pull/1611)) ## [9.0.0](https://github.com/GradleUp/shadow/compare/9.0.0) - 2025-08-07 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index fbdc57f51..4bbb9456c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsNone import com.github.jengelman.gradle.plugins.shadow.util.containsOnly @@ -213,6 +214,38 @@ class MinimizeTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/1610", + ) + @Test + fun excludeCircularDependencies() { + publishArtifactCD(circular = true) + + val dependency = "'my:d:1.0'" + projectScript.appendText( + """ + dependencies { + implementation $dependency + } + $shadowJarTask { + minimize { + exclude(dependency($dependency)) + } + } + """.trimIndent(), + ) + + run(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + containsOnly( + "c.properties", + "d.properties", + *manifestEntries, + ) + } + } + @ParameterizedTest @ValueSource(booleans = [false, true]) fun enableMinimizationByCliOption(enable: Boolean) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt index 506ef562e..a38ac71f0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt @@ -13,12 +13,14 @@ internal class MinimizeDependencyFilter( excludedDependencies: MutableSet, ) { dependencies.forEach { - if (it.isIncluded() && !isParentExcluded(excludedDependencies, it)) { + val added = if (it.isIncluded() && !isParentExcluded(excludedDependencies, it)) { includedDependencies.add(it) } else { excludedDependencies.add(it) } - resolve(it.children, includedDependencies, excludedDependencies) + if (added) { + resolve(it.children, includedDependencies, excludedDependencies) + } } } From 75a86882c955c0dc77fb79d00529e631584b256d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 9 Aug 2025 17:26:48 +0800 Subject: [PATCH 643/941] Cleanups of resolve (#1620) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../shadow/internal/DefaultDependencyFilter.kt | 6 +++--- .../shadow/internal/MinimizeDependencyFilter.kt | 17 +++++------------ .../plugins/shadow/tasks/DependencyFilter.kt | 12 ++++++++---- 3 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt index 0333c869a..4033738dd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt @@ -12,10 +12,10 @@ internal class DefaultDependencyFilter( includedDependencies: MutableSet, excludedDependencies: MutableSet, ) { - dependencies.forEach { - val added = if (it.isIncluded()) includedDependencies.add(it) else excludedDependencies.add(it) + dependencies.forEach { dep -> + val added = if (dep.isIncluded()) includedDependencies.add(dep) else excludedDependencies.add(dep) if (added) { - resolve(it.children, includedDependencies, excludedDependencies) + resolve(dep.children, includedDependencies, excludedDependencies) } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt index a38ac71f0..0ee204f86 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt @@ -12,22 +12,15 @@ internal class MinimizeDependencyFilter( includedDependencies: MutableSet, excludedDependencies: MutableSet, ) { - dependencies.forEach { - val added = if (it.isIncluded() && !isParentExcluded(excludedDependencies, it)) { - includedDependencies.add(it) + dependencies.forEach { dep -> + val added = if (dep.isIncluded() && !excludedDependencies.any { it in dep.parents }) { + includedDependencies.add(dep) } else { - excludedDependencies.add(it) + excludedDependencies.add(dep) } if (added) { - resolve(it.children, includedDependencies, excludedDependencies) + resolve(dep.children, includedDependencies, excludedDependencies) } } } - - private fun isParentExcluded( - excludedDependencies: Set, - dependency: ResolvedDependency, - ): Boolean { - return excludedDependencies.any { it in dependency.parents } - } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index 75a1a3529..43f4677c6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -56,11 +56,15 @@ public interface DependencyFilter : Serializable { ) override fun resolve(configuration: Configuration): FileCollection { - val included = mutableSetOf() - val excluded = mutableSetOf() - resolve(configuration.resolvedConfiguration.firstLevelModuleDependencies, included, excluded) + val includes = mutableSetOf() + val excludes = mutableSetOf() + resolve( + dependencies = configuration.resolvedConfiguration.firstLevelModuleDependencies, + includedDependencies = includes, + excludedDependencies = excludes, + ) return project.files(configuration.files) - - project.files(excluded.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) + project.files(excludes.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) } override fun resolve(configurations: Collection): FileCollection { From 0686a8f330445834e45a45aa962b01a2d3b065ee Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 9 Aug 2025 17:56:47 +0800 Subject: [PATCH 644/941] Prepare changelog for 9.0.1 and polish the items of 9.0.0 (#1621) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ffdbb8106..28ee8cabe 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,12 +3,17 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0...HEAD) - 2025-xx-xx +!!! note + + If you are upgrading from 8.x versions, please read 9.0.0 release notes first. + ### Changed - Improve the error message for empty `mainClassName`. ([#1601](https://github.com/GradleUp/shadow/pull/1601)) - Default `duplicatesStrategy` back to `EXCLUDE`. ([#1617](https://github.com/GradleUp/shadow/pull/1617)) - This strategy is consistent with 8.x series behavior, which is more compatible for most users upgrading. - For most `ResourceTransformer` users, you need to override the strategy to `INCLUDE` to make them work. + - Strongly suggest declaring the `duplicatesStrategy` explicitly in your `ShadowJar` configuration to avoid confusion. - See more details about the strategies at [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy). ### Fixed @@ -62,7 +67,7 @@ - Let `assemble` depend on `shadowJar`. ([#1524](https://github.com/GradleUp/shadow/pull/1524)) - Fail build when inputting AAR files or using Shadow with AGP. ([#1530](https://github.com/GradleUp/shadow/pull/1530)) - Add `PreserveFirstFoundResourceTransformer`. ([#1548](https://github.com/GradleUp/shadow/pull/1548)) - This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and + This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` and want to ensure that only the first found resource is included in the final JAR. - Fail build if the ZIP entries in the shadowed JAR are duplicate. ([#1552](https://github.com/GradleUp/shadow/pull/1552)) This feature is controlled by the `shadowJar.failOnDuplicateEntries` property, which is `false` by default. @@ -88,11 +93,12 @@ - **BREAKING CHANGE:** Change the default `duplicatesStrategy` from `EXCLUDE` to `INCLUDE`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - `ShadowJar` recognized `EXCLUDE` as the default, but the other strategies didn't work properly. - Now `ShadowJar` honors `INCLUDE` as the default, and aligns all the strategy behaviors with the Gradle side. - - `mergeServiceFiles` and `ServiceFileTransformer` do not work with `EXCLUDE`, as it will exclude extra service files to be merged. + - Some `ResourceTransformer`s (e.g. `ServiceFileTransformer`) do not work with `EXCLUDE`, as it will exclude duplicate resources to be merged. - Duplicate entries might be bundled due to this change, but you can reduce them by using the newly added `PreserveFirstFoundResourceTransformer`. - Use `filesMatching` to override the default strategy for specific files. - Set `failOnDuplicateEntries = true` to fail the build to check for duplicate entries. - See more details at [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy). + - **Note:** The default `duplicatesStrategy` is changed back to `EXCLUDE` in 9.0.1 release. - **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip @@ -185,10 +191,9 @@ tasks.shadowJar { // `isEnableRelocation` has been renamed to `enableAutoRelocation`. enableAutoRelocation = true - // The default `duplicatesStrategy` has been changed from `EXCLUDE` to `INCLUDE`. - duplicatesStrategy = DuplicatesStrategy.INCLUDE - // If you want to make `mergeServiceFiles` work, should leave the `duplicatesStrategy` as `INCLUDE`. + // If you want to make `mergeServiceFiles` work, should set the `duplicatesStrategy` as `INCLUDE`. // `EXCLUDE` will exclude extra service files to be merged. + duplicatesStrategy = DuplicatesStrategy.INCLUDE mergeServiceFiles() // If you leave `duplicatesStrategy` as `INCLUDE`, you can use the new `PreserveFirstFoundResourceTransformer` // to ensure that only the first found resource is included in the final JAR. Or duplicate entries will be bundled. @@ -207,7 +212,7 @@ tasks.shadowJar { If you used Shadow for merging service files, the following steps are recommended: -1. Make sure to leave `duplicatesStrategy` as default (`INCLUDE`) or `WARN`. +1. Make sure to leave `duplicatesStrategy` as `INCLUDE` or `WARN`. 2. Apply `mergeServiceFiles` or `ServiceFileTransformer` stuff as you did in your previous setup. 3. Diff the JARs from upgrading or not. 4. Remove the extra entries that are added by `INCLUDE` by `filesMatching` or `PreserveFirstFoundResourceTransformer`. From bbc693ad92532368e8c8600f35a4cfbab2703df4 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 9 Aug 2025 17:58:05 +0800 Subject: [PATCH 645/941] Fix the header link for version 9.0.0 --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 28ee8cabe..fc41164d9 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -21,7 +21,7 @@ - Fix the regression of can't shadow directory inputs. ([#1606](https://github.com/GradleUp/shadow/pull/1606)) - Fix the regression of `MinimizeDependencyFilter`. ([#1611](https://github.com/GradleUp/shadow/pull/1611)) -## [9.0.0](https://github.com/GradleUp/shadow/compare/9.0.0) - 2025-08-07 +## [9.0.0](https://github.com/GradleUp/shadow/releases/tag/9.0.0) - 2025-08-07 !!! warning From 9b51f5f410a158f04b7c789baf66105b7b3180b9 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 9 Aug 2025 17:58:34 +0800 Subject: [PATCH 646/941] Prepare version 9.0.1 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index fc41164d9..0ce92f7e4 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.0...HEAD) - 2025-xx-xx +## [9.0.1](https://github.com/GradleUp/shadow/releases/tag/9.0.1) - 2025-08-09 !!! note diff --git a/gradle.properties b/gradle.properties index 1b7458656..6c385538c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.1-SNAPSHOT +VERSION_NAME=9.0.1 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 8a5b15a89993af6299961f6b4b5491b1ede6b072 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 9 Aug 2025 17:59:24 +0800 Subject: [PATCH 647/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 0ce92f7e4..59f17b215 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.1...HEAD) - 2025-xx-xx + + ## [9.0.1](https://github.com/GradleUp/shadow/releases/tag/9.0.1) - 2025-08-09 !!! note diff --git a/gradle.properties b/gradle.properties index 6c385538c..38e449b17 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.1 +VERSION_NAME=9.0.2-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 2c104019ffcd8a9e7df4234d0dc28b84100b6496 Mon Sep 17 00:00:00 2001 From: xEricL <37921711+xEricL@users.noreply.github.com> Date: Sat, 9 Aug 2025 22:33:55 -0400 Subject: [PATCH 648/941] Fix missing space in ApacheNoticeResourceTransformer preamble causing malformed NOTICE header (#1623) * fix: missing space between default preamble1 and projectName in ApacheNoticeResourceTransformer * update docs/changes/README.md * Cleanups * Test `preamble1ShouldHaveATrailingSpace` --------- Co-authored-by: Goooler --- docs/changes/README.md | 3 ++ .../ApacheNoticeResourceTransformer.kt | 2 +- .../ApacheNoticeResourceTransformerTest.kt | 28 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 59f17b215..216e570e6 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.1...HEAD) - 2025-xx-xx +### Fixed + +- Fix missing space in `ApacheNoticeResourceTransformer` preamble causing malformed NOTICE header. ([#1623](https://github.com/GradleUp/shadow/pull/1623)). ## [9.0.1](https://github.com/GradleUp/shadow/releases/tag/9.0.1) - 2025-08-09 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 409cb884f..44a92f1eb 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -43,7 +43,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( // ------------------------------------------------------------------ // NOTICE file corresponding to the section 4d of The Apache License, // Version 2.0, in this case for - """.trimIndent(), + """.trimIndent() + " ", // The space is important for formatting. ) @get:Input diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt index 566dc5c78..75418b594 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -1,9 +1,15 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat +import assertk.assertions.contains import assertk.assertions.isFalse import assertk.assertions.isTrue import assertk.fail +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.nio.charset.Charset +import java.util.zip.ZipInputStream +import org.apache.tools.zip.ZipOutputStream import org.junit.jupiter.api.Test /** @@ -25,6 +31,28 @@ class ApacheNoticeResourceTransformerTest : BaseTransformerTest Date: Sun, 10 Aug 2025 14:53:46 +0800 Subject: [PATCH 649/941] Prefer com.gradle.plugin-publish in publishing shadowed Gradle plugin examples (#1625) --- docs/gradle-plugins/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/gradle-plugins/README.md b/docs/gradle-plugins/README.md index c717cf3d9..e0f4a30fe 100644 --- a/docs/gradle-plugins/README.md +++ b/docs/gradle-plugins/README.md @@ -15,7 +15,7 @@ task for relocation. ```kotlin plugins { - `java-gradle-plugin` // May have to apply the latest `com.gradle.plugin-publish` for better publishing support. + id("com.gradle.plugin-publish") version "latest" id("com.gradleup.shadow") } @@ -38,7 +38,7 @@ task for relocation. ```groovy plugins { - id 'java-gradle-plugin' // May have to apply the latest `com.gradle.plugin-publish` for better publishing support. + id 'com.gradle.plugin-publish' version 'latest' id 'com.gradleup.shadow' } From 06d09039145997930d5143912eb32d8456ce74d7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 11 Aug 2025 10:31:45 +0800 Subject: [PATCH 650/941] Use StringBuilder for ApacheNoticeResourceTransformer.transform (#1629) We don't need thread-safety for this function. --- .../shadow/transformers/ApacheNoticeResourceTransformer.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 44a92f1eb..016f75608 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -107,7 +107,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( val reader = context.inputStream.bufferedReader(charset) var line = reader.readLine() - val sb = StringBuffer() + val sb = StringBuilder() var currentOrg: MutableSet? = null var lineCount = 0 while (line != null) { From 60ab936136bc732fae4b456481b555b1ac2d8955 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 11 Aug 2025 11:14:47 +0800 Subject: [PATCH 651/941] Close zip entries in resource transformers (#1630) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../gradle/plugins/shadow/transformers/AppendingTransformer.kt | 1 + .../shadow/transformers/ComponentsXmlResourceTransformer.kt | 1 + .../plugins/shadow/transformers/IncludeResourceTransformer.kt | 2 ++ .../shadow/transformers/Log4j2PluginsCacheFileTransformer.kt | 1 + .../plugins/shadow/transformers/ManifestAppenderTransformer.kt | 2 ++ .../plugins/shadow/transformers/ManifestResourceTransformer.kt | 1 + .../plugins/shadow/transformers/XmlAppendingTransformer.kt | 1 + 7 files changed, 9 insertions(+) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index 5fda7760f..56a35be5f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -56,6 +56,7 @@ public open class AppendingTransformer @Inject constructor( it.toByteArray().inputStream().copyTo(os) it.reset() } + os.closeEntry() } public companion object { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index 0fb0428a7..9a4b731d3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -97,6 +97,7 @@ public open class ComponentsXmlResourceTransformer : ResourceTransformer { os.putNextEntry(zipEntry(COMPONENTS_XML_PATH, preserveFileTimestamps)) os.write(transformedResource) components.clear() + os.closeEntry() } override fun hasTransformedResource(): Boolean = components.isNotEmpty() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index 2f3128ff4..bfaf753bf 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -41,5 +41,7 @@ public open class IncludeResourceTransformer @Inject constructor( file.get().asFile.inputStream().use { inputStream -> inputStream.copyTo(os) } + + os.closeEntry() } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index cc2a25bfa..551ffd7a9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -60,6 +60,7 @@ public open class Log4j2PluginsCacheFileTransformer : ResourceTransformer { aggregator.writeCache(CloseShieldOutputStream.wrap(os)) } finally { deleteTempFiles() + os.closeEntry() } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 02b8c42a1..2b3d13dd9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -62,6 +62,8 @@ public open class ManifestAppenderTransformer @Inject constructor( os.write(EOL) attributes.empty() } + + os.closeEntry() } public open fun append(name: String, value: Comparable<*>) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 7165f95d7..285e6eb5a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -79,6 +79,7 @@ public open class ManifestResourceTransformer @Inject constructor( os.putNextEntry(zipEntry(JarFile.MANIFEST_NAME, preserveFileTimestamps)) manifest!!.write(os) + os.closeEntry() } public open fun attributes(attributes: Map) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index 391a30056..61e1287f1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -78,6 +78,7 @@ public open class XmlAppendingTransformer @Inject constructor( override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { os.putNextEntry(zipEntry(resource.get(), preserveFileTimestamps)) XMLOutputter(Format.getPrettyFormat()).output(doc, os) + os.closeEntry() doc = null } } From c19e9305d4b696f8a978b275e4b4cf80dc548261 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 11 Aug 2025 11:35:07 +0800 Subject: [PATCH 652/941] Fix using `ApacheNoticeResourceTransformer` without `projectName` (#1627) * Test `apacheNoticeTransformerWithRealDependencies` * Cleanups * Fallback copyright setting * Update test * Update changelog --- docs/changes/README.md | 1 + .../shadow/transformers/TransformersTest.kt | 60 ++++++++++++++++++- .../ApacheNoticeResourceTransformer.kt | 9 ++- 3 files changed, 67 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 216e570e6..bd72e42be 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -6,6 +6,7 @@ ### Fixed - Fix missing space in `ApacheNoticeResourceTransformer` preamble causing malformed NOTICE header. ([#1623](https://github.com/GradleUp/shadow/pull/1623)). +- Fix using `ApacheNoticeResourceTransformer` without `projectName`. ([#1627](https://github.com/GradleUp/shadow/pull/1627)). ## [9.0.1](https://github.com/GradleUp/shadow/releases/tag/9.0.1) - 2025-08-09 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index b79aef879..2c3f4013e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -211,6 +211,65 @@ class TransformersTest : BaseTransformerTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/1626", + ) + @Test + fun useApacheNoticeTransformerWithoutProjectName() { + val noticeEntry = "META-INF/NOTICE" + val one = buildJarOne { + insert( + noticeEntry, + """ + Apache Commons DBCP + Copyright 2001-2024 The Apache Software Foundation + + This product includes software developed at + The Apache Software Foundation (https://www.apache.org/). + """.trimIndent(), + ) + } + val two = buildJarTwo { + insert( + noticeEntry, + """ + Apache Commons Pool + Copyright 2001-2025 The Apache Software Foundation + + This product includes software developed at + The Apache Software Foundation (https://www.apache.org/). + """.trimIndent(), + ) + } + projectScript.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = "addHeader = false", + ), + ) + + run(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + containsOnly( + noticeEntry, + *manifestEntries, + ) + getContent(noticeEntry).transform { it.trim() }.isEqualTo( + """ + Apache Commons Pool + Copyright 2001-2025 The Apache Software Foundation + + This product includes software developed at + The Apache Software Foundation (https://www.apache.org/). + + Apache Commons DBCP + Copyright 2001-2024 The Apache Software Foundation + """.trimIndent(), + ) + } + } + @ParameterizedTest @MethodSource("transformerConfigProvider") fun otherTransformers(pair: Pair>) { @@ -268,7 +327,6 @@ class TransformersTest : BaseTransformerTest() { @JvmStatic fun transformerConfigProvider() = listOf( "" to ApacheLicenseResourceTransformer::class, - "" to ApacheNoticeResourceTransformer::class, "" to ComponentsXmlResourceTransformer::class, "" to ManifestAppenderTransformer::class, "" to ManifestResourceTransformer::class, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 016f75608..d8ecc9525 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -31,6 +31,11 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( private val organizationEntries = mutableMapOf>() private inline val charset get() = Charset.forName(charsetName.get()) + /** + * Fallback [copyright] as the [Property] value can't be changed in execution phase. + */ + private var fallbackCopyright: String? = null + @get:Input public open val projectName: Property = objectFactory.property("") @@ -129,7 +134,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( } else { val entry = sb.toString() if (entry.startsWith(projectName) && entry.contains("Copyright ")) { - copyright.set(entry) + fallbackCopyright = entry } if (currentOrg == null) { entries.add(entry) @@ -156,7 +161,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( override fun hasTransformedResource(): Boolean = true override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val copyright = copyright.orNull + val copyright = copyright.orNull ?: fallbackCopyright os.putNextEntry(zipEntry(NOTICE_PATH, preserveFileTimestamps)) From 7968bbde79f3a8a99b0035feca6dd886f667c48a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 11 Aug 2025 11:47:31 +0800 Subject: [PATCH 653/941] Fix extra indents of ApacheNoticeResourceTransformer output (#1628) * Replace writer stuff in `ApacheNoticeResourceTransformer.modifyOutputStream` * Trim notice string before writing * Update changelog * Reduce diff * Call `closeEntry` --- docs/changes/README.md | 1 + .../shadow/transformers/TransformersTest.kt | 2 +- .../ApacheNoticeResourceTransformer.kt | 28 +++++++++---------- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index bd72e42be..7e87a3a5b 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,6 +7,7 @@ - Fix missing space in `ApacheNoticeResourceTransformer` preamble causing malformed NOTICE header. ([#1623](https://github.com/GradleUp/shadow/pull/1623)). - Fix using `ApacheNoticeResourceTransformer` without `projectName`. ([#1627](https://github.com/GradleUp/shadow/pull/1627)). +- Fix extra indents of `ApacheNoticeResourceTransformer` output. ([#1628](https://github.com/GradleUp/shadow/pull/1628)). ## [9.0.1](https://github.com/GradleUp/shadow/releases/tag/9.0.1) - 2025-08-09 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 2c3f4013e..18bc0ed95 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -255,7 +255,7 @@ class TransformersTest : BaseTransformerTest() { noticeEntry, *manifestEntries, ) - getContent(noticeEntry).transform { it.trim() }.isEqualTo( + getContent(noticeEntry).isEqualTo( """ Apache Commons Pool Copyright 2001-2025 The Apache Software Foundation diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index d8ecc9525..8e3e50add 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry -import java.io.PrintWriter import java.nio.charset.Charset import java.text.SimpleDateFormat import java.util.Date @@ -162,36 +161,35 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { val copyright = copyright.orNull ?: fallbackCopyright - - os.putNextEntry(zipEntry(NOTICE_PATH, preserveFileTimestamps)) - - val writer = PrintWriter(os.writer(charset)) - + val sb = StringBuilder() var count = 0 for (line in entries) { count++ if (line == copyright && count != 2) continue if (count == 2 && copyright != null) { - writer.print(copyright) - writer.print('\n') + sb.append(copyright) + sb.append('\n') } else { - writer.print(line) - writer.print('\n') + sb.append(line) + sb.append('\n') } if (count == 3) { // Do org stuff. for ((key, value) in organizationEntries) { - writer.print(key) - writer.print('\n') + sb.append(key) + sb.append('\n') for (l in value) { - writer.print(l) + sb.append(l) } - writer.print('\n') + sb.append('\n') } } } - writer.flush() + os.putNextEntry(zipEntry(NOTICE_PATH, preserveFileTimestamps)) + os.write(sb.toString().trim().toByteArray(charset)) + os.closeEntry() + entries.clear() } From 39ad3f9ad207da7cda8051365d0eec6aae58d5af Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 11 Aug 2025 12:59:33 +0800 Subject: [PATCH 654/941] Revert "Add a test for using archiveFileName (#1148)" It's outdated, archive properties assertions are placed in `applyPlugin`. This reverts commit bd25e8dc81d39deae1fe658e572fedce19a40d45. --- .../gradle/plugins/shadow/JavaPluginsTest.kt | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index d27f618e1..08ad69903 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -711,32 +711,6 @@ class JavaPluginsTest : BasePluginTest() { ) } - @Test - fun worksWithArchiveFileName() { - val mainClassEntry = writeClass() - projectScript.appendText( - """ - dependencies { - implementation 'junit:junit:3.8.2' - } - $shadowJarTask { - archiveFileName = 'my-shadow.tar' - } - """.trimIndent(), - ) - - run(shadowJarPath) - - assertThat(jarPath("build/libs/my-shadow.tar")).useAll { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) - } - } - @Test fun inheritFromOtherManifest() { projectScript.appendText( From bf44bcf196d0e6c3290fbd0e435665a9646bf9c2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 11 Aug 2025 13:13:20 +0800 Subject: [PATCH 655/941] Tweak Class-Path related tests (#1631) * Tweak `registerShadowJarCommon` * Update examples * Update `supportZipCompressionStored` * Remove `classPathInManifestNotAddedIfEmpty` * Invert * Update `supportZipCompressions` * Add `classPathInManifestNotAddedIfEmpty` back --- docs/configuration/README.md | 6 +-- .../gradle/plugins/shadow/JavaPluginsTest.kt | 40 +++++++++++++------ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 8 ++-- 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 539334220..8086d4cd9 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -101,7 +101,7 @@ will propagate to the [`ShadowJar`][ShadowJar] tasks. ```kotlin tasks.jar { manifest { - attributes["Class-Path"] = "/libs/a.jar" + attributes["Class-Path"] = "/libs/foo.jar" } } ``` @@ -111,7 +111,7 @@ will propagate to the [`ShadowJar`][ShadowJar] tasks. ```groovy tasks.named('jar', Jar) { manifest { - attributes 'Class-Path': '/libs/a.jar' + attributes 'Class-Path': '/libs/foo.jar' } } ``` @@ -119,7 +119,7 @@ will propagate to the [`ShadowJar`][ShadowJar] tasks. Inspecting the `META-INF/MANIFEST.MF` entry in the JAR file will reveal the following attribute: ```property -Class-Path: /libs/a.jar +Class-Path: /libs/foo.jar ``` If it is desired to inherit a manifest from a JAR task other than the standard [`Jar`][Jar] task, the `inheritFrom` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 08ad69903..c61ec6f9a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -38,14 +38,17 @@ import kotlin.io.path.outputStream import kotlin.io.path.readText import kotlin.io.path.writeText import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.internal.tasks.JvmConstants import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.tasks.bundling.Jar +import org.gradle.api.tasks.bundling.ZipEntryCompression import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.EnumSource import org.junit.jupiter.params.provider.ValueSource class JavaPluginsTest : BasePluginTest() { @@ -435,23 +438,26 @@ class JavaPluginsTest : BasePluginTest() { run(shadowJarPath) - val value = outputShadowedJar.use { it.getMainAttr(classPathAttributeKey) } - assertThat(value).isNull() + assertThat(outputShadowedJar).useAll { + transform { it.mainAttrSize }.isEqualTo(1) + getMainAttr(classPathAttributeKey).isNull() + } } @Issue( "https://github.com/GradleUp/shadow/issues/65", ) - @Test - fun addShadowConfigurationToClassPathInManifest() { + @ParameterizedTest + @ValueSource(strings = [ShadowBasePlugin.CONFIGURATION_NAME, JvmConstants.IMPLEMENTATION_CONFIGURATION_NAME]) + fun addShadowConfigurationToClassPathInManifest(configuration: String) { projectScript.appendText( """ dependencies { - shadow 'junit:junit:3.8.2' + $configuration 'junit:junit:3.8.2' } $jarTask { manifest { - attributes '$classPathAttributeKey': '/libs/a.jar' + attributes '$classPathAttributeKey': '/libs/foo.jar' } } """.trimIndent(), @@ -459,8 +465,12 @@ class JavaPluginsTest : BasePluginTest() { run(shadowJarPath) - val value = outputShadowedJar.use { it.getMainAttr(classPathAttributeKey) } - assertThat(value).isEqualTo("/libs/a.jar junit-3.8.2.jar") + val actual = outputShadowedJar.use { it.getMainAttr(classPathAttributeKey) } + val expected = when (configuration) { + ShadowBasePlugin.CONFIGURATION_NAME -> "/libs/foo.jar junit-3.8.2.jar" + else -> "/libs/foo.jar" + } + assertThat(actual).isEqualTo(expected) } @Issue( @@ -485,16 +495,17 @@ class JavaPluginsTest : BasePluginTest() { @Issue( "https://github.com/GradleUp/shadow/issues/203", ) - @Test - fun supportZipCompressionStored() { + @ParameterizedTest + @EnumSource(ZipEntryCompression::class) + fun supportZipCompressions(method: ZipEntryCompression) { projectScript.appendText( """ dependencies { - shadow 'junit:junit:3.8.2' + implementation 'junit:junit:3.8.2' } $shadowJarTask { zip64 = true - entryCompression = org.gradle.api.tasks.bundling.ZipEntryCompression.STORED + entryCompression = ${ZipEntryCompression::class.java.canonicalName}.$method } """.trimIndent(), ) @@ -502,7 +513,10 @@ class JavaPluginsTest : BasePluginTest() { run(shadowJarPath) assertThat(outputShadowedJar).useAll { - transform { it.entries().toList() }.isNotEmpty() + containsOnly( + *junitEntries, + *manifestEntries, + ) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 301eab44b..4bdbf113b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -483,11 +483,11 @@ public abstract class ShadowJar : Jar() { @Suppress("EagerGradleConfiguration") // mergeSpec.from hasn't supported lazy configuration yet. task.manifest.inheritFrom(jarTask.get().manifest) - val attrProvider = jarTask.map { it.manifest.attributes[classPathAttributeKey]?.toString().orEmpty() } - val files = files(configurations.shadow) + val classPathAttr = jarTask.map { it.manifest.attributes[classPathAttributeKey]?.toString().orEmpty() } + val shadowFiles = files(configurations.shadow) task.doFirst { - if (!files.isEmpty) { - val attrs = listOf(attrProvider.getOrElse("")) + files.map { it.name } + if (!shadowFiles.isEmpty) { + val attrs = listOf(classPathAttr.get()) + shadowFiles.map { it.name } task.manifest.attributes[classPathAttributeKey] = attrs.joinToString(" ").trim() } } From 38c702fa35725a59e476bf2538b9dfb5a220e432 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 11 Aug 2025 15:39:07 +0800 Subject: [PATCH 656/941] Add names for doFirst and doLast (#1632) --- .../gradle/plugins/shadow/ShadowApplicationPlugin.kt | 6 +++--- .../jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt | 2 +- .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index a6d6743d6..91b7facf1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -84,7 +84,7 @@ public abstract class ShadowApplicationPlugin : Plugin { tasks.installShadowDist.configure { task -> val applicationName = providers.provider { applicationExtension.applicationName } - task.doFirst { + task.doFirst("Check installation directory") { if ( !task.destinationDir.listFiles().isNullOrEmpty() && ( @@ -99,7 +99,7 @@ public abstract class ShadowApplicationPlugin : Plugin { ) } } - task.doLast { + task.doLast("Set permissions for the start scripts") { task.eachFile { if (it.path == "bin/${applicationName.get()}") { it.permissions { permissions -> permissions.unix(UNIX_SCRIPT_PERMISSIONS) } @@ -131,7 +131,7 @@ public abstract class ShadowApplicationPlugin : Plugin { val mainClassName = applicationExtension.mainClass.convention("") tasks.shadowJar.configure { task -> task.inputs.property("mainClassName", mainClassName) - task.doFirst { + task.doFirst("Set $mainClassAttributeKey attribute in the manifest") { // Inject the attribute if it is not already present. if (!task.manifest.attributes.contains(mainClassAttributeKey)) { val realClass = mainClassName.orNull 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 68c09f746..5c1456cdc 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 @@ -44,7 +44,7 @@ public abstract class ShadowKmpPlugin : Plugin { // 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 { + task.doFirst("Set $mainClassAttributeKey attribute in the manifest") { val realClass = mainClassName.get().orNull if (!task.manifest.attributes.contains(mainClassAttributeKey) && !realClass.isNullOrEmpty()) { task.manifest.attributes[mainClassAttributeKey] = realClass diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 4bdbf113b..60ea1eb60 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -485,7 +485,7 @@ public abstract class ShadowJar : Jar() { task.manifest.inheritFrom(jarTask.get().manifest) val classPathAttr = jarTask.map { it.manifest.attributes[classPathAttributeKey]?.toString().orEmpty() } val shadowFiles = files(configurations.shadow) - task.doFirst { + task.doFirst("Set $classPathAttributeKey attribute in the manifest") { if (!shadowFiles.isEmpty) { val attrs = listOf(classPathAttr.get()) + shadowFiles.map { it.name } task.manifest.attributes[classPathAttributeKey] = attrs.joinToString(" ").trim() From 8cfbed4e9a52f62bdd4d975fa92cef5039e9eb64 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 11 Aug 2025 18:43:43 +0800 Subject: [PATCH 657/941] Note more about the default DuplicatesStrategy (#1634) --- docs/changes/README.md | 5 +++++ docs/configuration/merging/README.md | 10 ++++++---- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 11 ++++++----- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 7e87a3a5b..85a81a9b0 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -15,6 +15,11 @@ If you are upgrading from 8.x versions, please read 9.0.0 release notes first. +!!! tip + + You can diff the shadowed JARs when upgrading from 8.x to 9.x by using [Diffuse](https://github.com/JakeWharton/diffuse). + If there are any things missing in the changelog or the doc site, please report them to us. + ### Changed - Improve the error message for empty `mainClassName`. ([#1601](https://github.com/GradleUp/shadow/pull/1601)) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 0341aaec5..89b97421e 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -62,7 +62,7 @@ Different strategies will lead to different results for `foo/bar` files in the J ```kotlin tasks.shadowJar { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // The default strategy. mergeServiceFiles() } ``` @@ -71,7 +71,7 @@ Different strategies will lead to different results for `foo/bar` files in the J ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // The default strategy. mergeServiceFiles() } ``` @@ -86,9 +86,11 @@ to take: 2. Apply your [`ResourceTransformer`][ResourceTransformer]s. 3. Remove duplicate entries by - overriding the default strategy for specific files using [`filesMatching`][Jar.filesMatching] - - applying [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer] - - or something similar. + - or applying [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer] for specific files + - or write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates + - or mechanism similar. 4. Optionally, enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. +5. Optionally, use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. ## Basic ResourceTransformer Usage diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 60ea1eb60..61e2a57e5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -18,7 +18,6 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer -import com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoundResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer @@ -208,17 +207,19 @@ public abstract class ShadowJar : Jar() { * - [WARN]: **Warn** about duplicates in the build log, this behaves exactly as [INHERIT] otherwise. * * **NOTE:** The strategy takes precedence over transforming and relocating. - * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to [EXCLUDE], - * as the duplicate resource files fed for them are excluded beforehand. + * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to + * [EXCLUDE] (the default), as the duplicate resource files fed for them are excluded beforehand. * Want [ResourceTransformer]s and the strategy to work together? There are several steps to take: * * 1. Set the strategy to [INCLUDE] or [WARN]. * 2. Apply your [ResourceTransformer]s. * 3. Remove duplicate entries by * - overriding the default strategy for specific files using [filesMatching] - * - applying [PreserveFirstFoundResourceTransformer] - * - or something similar. + * - or applying `PreserveFirstFoundResourceTransformer` for specific files + * - or write your own `ResourceTransformer`s to handle duplicates + * - or mechanism similar. * 4. Optionally, enable [failOnDuplicateEntries] to check duplicate entries in the final JAR. + * 5. Optionally, use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. * * @see [filesMatching] * @see [DuplicatesStrategy] From b511836e79e4013957532d5475e752b15e0d3a2c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Aug 2025 13:44:33 +0000 Subject: [PATCH 658/941] Update actions/checkout action to v5 (#1635) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- .github/workflows/deploy.yml | 2 +- .github/workflows/links.yml | 2 +- .github/workflows/release.yml | 2 +- .github/workflows/update-start-scripts.yml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c1d2d0def..a80bff77e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,7 +31,7 @@ jobs: java: 21 runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-java@v4 with: distribution: 'zulu' @@ -52,7 +52,7 @@ jobs: runs-on: ubuntu-24.04-arm if: github.event.repository.fork == false && github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-java@v4 with: distribution: 'zulu' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 390183dfb..8fa3f31aa 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,7 +14,7 @@ jobs: id-token: write pages: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: gradle/actions/setup-gradle@v4 with: cache-read-only: true diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index d752a2e77..fd87e128b 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -21,7 +21,7 @@ jobs: check-links: runs-on: ubuntu-24.04-arm steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: gradle/actions/setup-gradle@v4 with: cache-read-only: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e948245bf..00c4345d3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-java@v4 with: distribution: 'zulu' diff --git a/.github/workflows/update-start-scripts.yml b/.github/workflows/update-start-scripts.yml index 492e10d24..8278ae41b 100644 --- a/.github/workflows/update-start-scripts.yml +++ b/.github/workflows/update-start-scripts.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-24.04-arm if: github.event.repository.fork == false steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-java@v4 with: distribution: 'zulu' From 0187e753f555f32c8a829abc42a68bbe030eb0ea Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 12 Aug 2025 10:52:15 +0800 Subject: [PATCH 659/941] Fix resolving BOM dependencies when minimize is enabled (#1637) * Test `minimizeBomDependency` * Skip `ExternalModuleDependency` * Update changelog * Avoid NPE * Tweak test --- docs/changes/README.md | 1 + .../gradle/plugins/shadow/MinimizeTest.kt | 30 +++++++++++++++++++ .../plugins/shadow/internal/UnusedTracker.kt | 6 +++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 85a81a9b0..7a57af558 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -8,6 +8,7 @@ - Fix missing space in `ApacheNoticeResourceTransformer` preamble causing malformed NOTICE header. ([#1623](https://github.com/GradleUp/shadow/pull/1623)). - Fix using `ApacheNoticeResourceTransformer` without `projectName`. ([#1627](https://github.com/GradleUp/shadow/pull/1627)). - Fix extra indents of `ApacheNoticeResourceTransformer` output. ([#1628](https://github.com/GradleUp/shadow/pull/1628)). +- Fix resolving BOM dependencies when `minimize` is enabled. ([#1637](https://github.com/GradleUp/shadow/pull/1637)). ## [9.0.1](https://github.com/GradleUp/shadow/releases/tag/9.0.1) - 2025-08-09 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index 4bbb9456c..897dacd4d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -279,6 +279,36 @@ class MinimizeTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/1636", + ) + @Test + fun minimizeBomDependency() { + writeApiLibAndImplModules() + path("impl/build.gradle").appendText( + """ + dependencies { + api platform('org.jetbrains.kotlin:kotlin-bom:2.2.0') + } + """.trimIndent(), + ) + + run(":impl:$SHADOW_JAR_TASK_NAME") + + assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { + containsAtLeast( + "api/", + "lib/", + "impl/", + "impl/SimpleEntity.class", + "api/Entity.class", + "api/UnusedEntity.class", + "lib/LibEntity.class", + *manifestEntries, + ) + } + } + private fun writeApiLibAndImplModules() { settingsScript.appendText( """ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index b82aaeda7..1c6f9d97a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -4,6 +4,7 @@ import java.io.File import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.ExternalModuleDependency import org.gradle.api.artifacts.FileCollectionDependency import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.file.FileCollection @@ -54,9 +55,12 @@ internal class UnusedTracker( is FileCollectionDependency -> { apiJars.addAll(dep.files) } + // Skip BOM dependencies and other non-JAR dependencies. + is ExternalModuleDependency -> Unit else -> { addJar(runtimeConfiguration, dep, apiJars) - apiJars.add(runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } as File) + val jarFile = runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } ?: return@forEach + apiJars.add(jarFile) } } } From b0fbcba190e094cbece9c0da35e486b138416df8 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 12 Aug 2025 21:51:04 +0800 Subject: [PATCH 660/941] Support publishing and testing BOMs in tests (#1639) * Update `AppendableMavenRepository` to support BOM * Revert "Update `AppendableMavenRepository` to support BOM" This reverts commit e6233b79a6c14c8d238dbadb75e7170214920653. * Explicit `JarModule` * Explicit `BomModule` * Refactor `createPublication` * Support `BomModule` publications * Cleanups * Publish and use `my:bom:1.0` * Revert "Fix resolving BOM dependencies when minimize is enabled" This reverts commit 0187e753 * Reapply "Fix resolving BOM dependencies when minimize is enabled" This reverts commit 8c50702fe36c34181aa2b677afa6aadb364c878c. * Check root * Cleanups --- .../gradle/plugins/shadow/BasePluginTest.kt | 16 +- .../gradle/plugins/shadow/JavaPluginsTest.kt | 14 +- .../gradle/plugins/shadow/MinimizeTest.kt | 2 +- .../gradle/plugins/shadow/RelocationTest.kt | 2 +- .../ServiceFileTransformerTest.kt | 2 +- .../shadow/util/AppendableMavenRepository.kt | 196 +++++++++++++----- 6 files changed, 159 insertions(+), 73 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 be37ed3b3..40e62fad4 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 @@ -76,21 +76,25 @@ abstract class BasePluginTest { @BeforeAll open fun doFirst() { localRepo = AppendableMavenRepository( - createTempDirectory().resolve("local-maven-repo"), + createTempDirectory().resolve("local-maven-repo").createDirectories(), runner(projectDir = null), ) - localRepo.module("junit", "junit", "3.8.2") { + localRepo.jarModule("junit", "junit", "3.8.2") { useJar(junitJar) - }.module("my", "a", "1.0") { + }.jarModule("my", "a", "1.0") { buildJar { insert("a.properties", "a") insert("a2.properties", "a2") } - }.module("my", "b", "1.0") { + }.jarModule("my", "b", "1.0") { buildJar { insert("b.properties", "b") } }.publish() + localRepo.bomModule("my", "bom", "1.0") { + addDependency("my", "a", "1.0") + addDependency("my", "b", "1.0") + }.publish() artifactAJar = path("my/a/1.0/a-1.0.jar", parent = localRepo.root) artifactBJar = path("my/b/1.0/b-1.0.jar", parent = localRepo.root) @@ -195,14 +199,14 @@ abstract class BasePluginTest { } fun publishArtifactCD(circular: Boolean = false) { - localRepo.module("my", "c", "1.0") { + localRepo.jarModule("my", "c", "1.0") { buildJar { insert("c.properties", "c") } if (circular) { addDependency("my", "d", "1.0") } - }.module("my", "d", "1.0") { + }.jarModule("my", "d", "1.0") { buildJar { insert("d.properties", "d") } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index c61ec6f9a..441116653 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -271,7 +271,7 @@ class JavaPluginsTest : BasePluginTest() { ) @Test fun excludeSomeMetaInfFilesByDefault() { - localRepo.module("my", "a", "1.0") { + localRepo.jarModule("my", "a", "1.0") { buildJar { insert("a.properties", "a") insert("META-INF/INDEX.LIST", "JarIndex-Version: 1.0") @@ -335,20 +335,20 @@ class JavaPluginsTest : BasePluginTest() { @Test fun includeJavaLibraryConfigurationsByDefault() { - localRepo.module("my", "api", "1.0") { + localRepo.jarModule("my", "api", "1.0") { buildJar { insert("api.properties", "api") } - }.module("my", "implementation-dep", "1.0") { + }.jarModule("my", "implementation-dep", "1.0") { buildJar { insert("implementation-dep.properties", "implementation-dep") } - }.module("my", "implementation", "1.0") { + }.jarModule("my", "implementation", "1.0") { buildJar { insert("implementation.properties", "implementation") } addDependency("my", "implementation-dep", "1.0") - }.module("my", "runtimeOnly", "1.0") { + }.jarModule("my", "runtimeOnly", "1.0") { buildJar { insert("runtimeOnly.properties", "runtimeOnly") } @@ -401,11 +401,11 @@ class JavaPluginsTest : BasePluginTest() { @Test fun defaultCopyingStrategy() { - localRepo.module("my", "a", "1.0") { + localRepo.jarModule("my", "a", "1.0") { buildJar { insert(manifestEntry, "MANIFEST A") } - }.module("my", "b", "1.0") { + }.jarModule("my", "b", "1.0") { buildJar { insert(manifestEntry, "MANIFEST B") } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index 897dacd4d..38bdbc420 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -288,7 +288,7 @@ class MinimizeTest : BasePluginTest() { path("impl/build.gradle").appendText( """ dependencies { - api platform('org.jetbrains.kotlin:kotlin-bom:2.2.0') + api platform('my:bom:1.0') } """.trimIndent(), ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 6a15cfa20..809001652 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -284,7 +284,7 @@ class RelocationTest : BasePluginTest() { ) @Test fun relocateResourceFiles() { - localRepo.module("my", "dep", "1.0") { + localRepo.jarModule("my", "dep", "1.0") { buildJar { insert("foo/dep.properties", "c") } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 5992f4d83..9e3db1b67 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -169,7 +169,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { val one = buildJarOne { insert(servicesBarEntry, CONTENT_ONE) } - localRepo.module("foo", "bar", "1.0") { + localRepo.jarModule("foo", "bar", "1.0") { buildJar { insert(servicesBarEntry, CONTENT_TWO) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index c81e438e4..90661f740 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -2,7 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.util import com.github.jengelman.gradle.plugins.shadow.BasePluginTest.Companion.commonArguments import java.nio.file.Path -import kotlin.io.path.createDirectories +import kotlin.io.path.createDirectory import kotlin.io.path.createFile import kotlin.io.path.exists import kotlin.io.path.invariantSeparatorsPathString @@ -17,84 +17,151 @@ class AppendableMavenRepository( val root: Path, private val gradleRunner: GradleRunner, ) { - private val projectBuildScript: Path private val modules = mutableListOf() + private val jarsDir: Path init { - root.resolve("temp").createDirectories() + check(root.exists()) { "Maven repository root directory does not exist: $root" } + root.resolve("settings.gradle").createFile() .writeText("rootProject.name = '${root.name}'") - projectBuildScript = root.resolve("build.gradle").createFile() + root.resolve("build.gradle").createFile() + jarsDir = root.resolve("jars").createDirectory() + } + + fun jarModule( + groupId: String, + artifactId: String, + version: String, + action: JarModule.() -> Unit, + ) = apply { + modules += JarModule(groupId, artifactId, version).also(action) } - fun module( + fun bomModule( groupId: String, artifactId: String, version: String, - action: Module.() -> Unit, + action: BomModule.() -> Unit, ) = apply { - modules += Module(groupId, artifactId, version).also(action) + modules += BomModule(groupId, artifactId, version).also(action) } fun publish() { - if (modules.isEmpty()) return - projectBuildScript.writeText( - """ + check(modules.isNotEmpty()) { + "No modules to publish. Please add at least one module." + } + val groups = modules.groupBy { it::class }.entries + check(groups.size == 1) { + "Only one type of module can be published at a time." + } + + @Suppress("UNCHECKED_CAST") + when (val type = groups.first().key) { + JarModule::class -> { + publishJarModules(modules as List) + } + BomModule::class -> { + publishBomModules(modules as List) + } + else -> error("Unsupported module type: $type") + } + } + + private fun publishJarModules(jarModules: List) { + val mavenPublications = jarModules.joinToString(lineSeparator) { module -> + var index = -1 + val nodes = module.dependencies.joinToString(lineSeparator) { + index++ + val node = "dependencyNode$index" + """ + def $node = dependenciesNode.appendNode('dependency') + $node.appendNode('groupId', '${it.groupId}') + $node.appendNode('artifactId', '${it.artifactId}') + $node.appendNode('version', '${it.version}') + $node.appendNode('scope', '${it.scope}') + """.trimIndent() + } + module.createMavenPublication( + """ + artifact '${module.artifactPath}' + pom.withXml { xml -> + def dependenciesNode = xml.asNode().get('dependencies') ?: xml.asNode().appendNode('dependencies') + $nodes + } + """.trimIndent(), + ) + } + val scriptContent = """ + plugins { + id 'maven-publish' + } + publishing { + publications { + $mavenPublications + } + repositories { + maven { url = '${root.toUri()}' } + } + } + """.trimIndent() + runPublish(scriptContent) + modules.clear() + } + + private fun publishBomModules(bomModules: List) { + // BOM modules are published one by one. + bomModules.forEach { module -> + val scriptContent = """ plugins { id 'maven-publish' + id 'java-platform' + } + dependencies { + constraints { + ${module.dependencies.joinToString(lineSeparator) { "api '${it.coordinate}'" }} + } } publishing { publications { - ${modules.joinToString(lineSeparator) { createPublication(it) }} + ${module.createMavenPublication("from components.javaPlatform")} } repositories { maven { url = '${root.toUri()}' } } } - """.trimIndent(), - ) - gradleRunner.withProjectDir(root.toFile()).withArguments(commonArguments + "publish").build() - modules.clear() - } - - private fun createPublication(module: Module) = with(module) { - val outputJar = build() - val pubName = outputJar.name.replace(".", "") - - var index = -1 - val nodes = dependencies.joinToString(lineSeparator) { - index++ - val node = "dependencyNode$index" - """ - def $node = dependenciesNode.appendNode('dependency') - $node.appendNode('groupId', '${it.groupId}') - $node.appendNode('artifactId', '${it.artifactId}') - $node.appendNode('version', '${it.version}') - $node.appendNode('scope', '${it.scope}') """.trimIndent() + runPublish(scriptContent) } + modules.clear() + } - """ - create('$pubName', MavenPublication) { + private fun Module.createMavenPublication( + block: String, + ): String { + return """ + create('${coordinate.replace(":", "")}', MavenPublication) { artifactId = '$artifactId' groupId = '$groupId' version = '$version' - artifact '${outputJar.invariantSeparatorsPathString}' - pom.withXml { xml -> - def dependenciesNode = xml.asNode().get('dependencies') ?: xml.asNode().appendNode('dependencies') - $nodes - } + $block } - """.trimIndent() + lineSeparator + """.trimIndent() + } + + private fun runPublish(scriptContent: String) { + root.resolve("build.gradle").writeText(scriptContent) + gradleRunner.withProjectDir(root.toFile()) + .withArguments(commonArguments + "publish") + .build() } - inner class Module( + sealed class Module( groupId: String, artifactId: String, version: String, ) : Model() { - private val coordinate = "$groupId:$artifactId:$version" - private lateinit var existingJar: Path + val coordinate = "$groupId:$artifactId:$version" init { this.groupId = groupId @@ -102,15 +169,6 @@ class AppendableMavenRepository( this.version = version } - fun useJar(existingJar: Path) { - this.existingJar = existingJar - } - - fun buildJar(builder: JarBuilder.() -> Unit) { - val jarName = coordinate.replace(":", "-") + ".jar" - existingJar = JarBuilder(root.resolve("temp/$jarName")).apply(builder).write() - } - fun addDependency(groupId: String, artifactId: String, version: String, scope: String = "runtime") { val dependency = Dependency().also { it.groupId = groupId @@ -120,13 +178,37 @@ class AppendableMavenRepository( } addDependency(dependency) } + } - fun build(): Path { - check(::existingJar.isInitialized) { "No jar file provided for $coordinate" } - return existingJar.also { - check(it.exists()) { "Jar file doesn't exist for $coordinate in: $it" } - check(it.isRegularFile()) { "Jar is not a regular file for $coordinate in: $it" } - } + inner class JarModule( + groupId: String, + artifactId: String, + version: String, + ) : Module(groupId, artifactId, version) { + private var existingJar: Path? = null + + val artifactPath: String + get() = existingJar?.also { + check(it.exists() && it.isRegularFile()) { "Jar file does not exist or is not a regular file: $it" } + }?.invariantSeparatorsPathString ?: error("No jar file provided for $coordinate") + + fun useJar(existingJar: Path) { + this.existingJar = existingJar + } + + fun buildJar(builder: JarBuilder.() -> Unit) { + val jarPath = jarsDir.resolve(coordinate.replace(":", "-") + ".jar") + existingJar = JarBuilder(jarPath).apply(builder).write() + } + } + + class BomModule( + groupId: String, + artifactId: String, + version: String, + ) : Module(groupId, artifactId, version) { + init { + packaging = "pom" } } } From 6823777f32fd642c521d42c149676cf664567f95 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 12 Aug 2025 22:40:54 +0800 Subject: [PATCH 661/941] Tweak runtimeOnly related tests (#1643) * Remove `defaultCopyingStrategy` * Merge `doNotIncludeCompileOnlyConfigurationByDefault` * Update `excludeGradleApiByDefault` * Revert "Update `excludeGradleApiByDefault`" This reverts commit 6192a1ddfb34185c2e06f0b9eaebb178c17cf5db. --- .../gradle/plugins/shadow/JavaPluginsTest.kt | 49 +------------------ 1 file changed, 1 insertion(+), 48 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 441116653..f6239e499 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -319,6 +319,7 @@ class JavaPluginsTest : BasePluginTest() { dependencies { runtimeOnly 'my:a:1.0' shadow 'my:b:1.0' + compileOnly 'my:b:1.0' } """.trimIndent(), ) @@ -378,54 +379,6 @@ class JavaPluginsTest : BasePluginTest() { } } - @Test - fun doNotIncludeCompileOnlyConfigurationByDefault() { - projectScript.appendText( - """ - dependencies { - runtimeOnly 'my:a:1.0' - compileOnly 'my:b:1.0' - } - """.trimIndent(), - ) - - run(shadowJarPath) - - assertThat(outputShadowedJar).useAll { - containsOnly( - *entriesInA, - *manifestEntries, - ) - } - } - - @Test - fun defaultCopyingStrategy() { - localRepo.jarModule("my", "a", "1.0") { - buildJar { - insert(manifestEntry, "MANIFEST A") - } - }.jarModule("my", "b", "1.0") { - buildJar { - insert(manifestEntry, "MANIFEST B") - } - }.publish() - - projectScript.appendText( - """ - dependencies { - runtimeOnly 'my:a:1.0' - runtimeOnly 'my:b:1.0' - } - """.trimIndent(), - ) - - run(shadowJarPath) - - val entries = outputShadowedJar.use { it.entries().toList() } - assertThat(entries.size).isEqualTo(2) - } - @Test fun classPathInManifestNotAddedIfEmpty() { projectScript.appendText( From 8f3f8b84bfc876863e635af03dcc77a463e3456e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 12 Aug 2025 23:24:46 +0800 Subject: [PATCH 662/941] Tweak artifacts related tests (#1644) * Remove usages of artifactAJar and artifactBJar * Const entriesInA, entriesInB, and entriesInAB * Tweak tests --- .../gradle/plugins/shadow/BasePluginTest.kt | 12 +++--------- .../jengelman/gradle/plugins/shadow/CachingTest.kt | 8 +++++--- .../gradle/plugins/shadow/JavaPluginsTest.kt | 13 +++++++++---- 3 files changed, 17 insertions(+), 16 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 40e62fad4..50044b436 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 @@ -55,12 +55,6 @@ abstract class BasePluginTest { private set lateinit var artifactBJar: Path private set - lateinit var entriesInA: Array - private set - lateinit var entriesInB: Array - private set - lateinit var entriesInAB: Array - private set val shadowJarPath = ":$SHADOW_JAR_TASK_NAME" val serverShadowJarPath = ":server:$SHADOW_JAR_TASK_NAME" @@ -98,9 +92,6 @@ abstract class BasePluginTest { artifactAJar = path("my/a/1.0/a-1.0.jar", parent = localRepo.root) artifactBJar = path("my/b/1.0/b-1.0.jar", parent = localRepo.root) - entriesInA = arrayOf("a.properties", "a2.properties") - entriesInB = arrayOf("b.properties") - entriesInAB = entriesInA + entriesInB } @BeforeEach @@ -420,6 +411,9 @@ abstract class BasePluginTest { Path(gradleUserHome, "testkit") } + val entriesInA = arrayOf("a.properties", "a2.properties") + val entriesInB = arrayOf("b.properties") + val entriesInAB = entriesInA + entriesInB val junitJar: Path = requireResourceAsPath("junit-3.8.2.jar") val junitRawEntries: List = JarPath(junitJar) .use { it.entries().toList() } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt index ca84a2b28..12b85a63a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -34,7 +34,8 @@ class CachingTest : BasePluginTest() { projectScript.appendText( """ dependencies { - ${implementationFiles(artifactAJar, artifactBJar)} + implementation 'my:a:1.0' + implementation 'my:b:1.0' } """.trimIndent(), ) @@ -46,7 +47,7 @@ class CachingTest : BasePluginTest() { ) } - val replaced = projectScript.readText().replace(implementationFiles(artifactBJar), "") + val replaced = projectScript.readText().replace("implementation 'my:b:1.0'", "") projectScript.writeText(replaced) assertCompositeExecutions { @@ -62,7 +63,8 @@ class CachingTest : BasePluginTest() { projectScript.appendText( """ dependencies { - ${implementationFiles(artifactAJar, artifactBJar)} + implementation 'my:a:1.0' + implementation 'my:b:1.0' } """.trimIndent() + lineSeparator, ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index f6239e499..de3ec62f4 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -714,7 +714,10 @@ class JavaPluginsTest : BasePluginTest() { projectScript.appendText( """ $shadowJarTask { - from(file('${artifactAJar.invariantSeparatorsPathString}')) { + from(file('${artifactAJar.invariantSeparatorsPathString}')) { // Without unzipping. + into('META-INF') + } + from(zipTree(file('${artifactBJar.invariantSeparatorsPathString}'))) { // With unzipping. into('META-INF') } from('Foo') { @@ -732,10 +735,12 @@ class JavaPluginsTest : BasePluginTest() { "Bar/", "Bar/Foo", "META-INF/a-1.0.jar", + "META-INF/b.properties", mainClassEntry, *manifestEntries, ) getContent("Bar/Foo").isEqualTo("Foo") + getContent("META-INF/b.properties").isEqualTo("b") } val unzipped = path("unzipped") outputShadowedJar.use { @@ -866,7 +871,7 @@ class JavaPluginsTest : BasePluginTest() { projectScript.appendText( """ dependencies { - ${implementationFiles(artifactAJar)} + implementation 'my:a:1.0' } $shadowJarTask { duplicatesStrategy = DuplicatesStrategy.INCLUDE @@ -890,11 +895,11 @@ class JavaPluginsTest : BasePluginTest() { @ParameterizedTest @ValueSource(booleans = [false, true]) fun failBuildIfDuplicateEntriesByCliOption(enable: Boolean) { - path("src/main/resources/a.properties").writeText("invalid a") + path("src/main/resources/a.properties").writeText("project a") projectScript.appendText( """ dependencies { - ${implementationFiles(artifactAJar)} + implementation 'my:a:1.0' } $shadowJarTask { duplicatesStrategy = DuplicatesStrategy.INCLUDE From 1cf910fdd9a3d0030c7ce4b5923e496dc4dbb8cc Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 13 Aug 2025 00:09:06 +0800 Subject: [PATCH 663/941] Group const values in tests --- .../gradle/plugins/shadow/BasePluginTest.kt | 17 ++++++++--------- .../gradle/plugins/shadow/JavaPluginsTest.kt | 8 ++++---- .../gradle/plugins/shadow/KotlinPluginsTest.kt | 2 +- .../gradle/plugins/shadow/RelocationTest.kt | 2 +- 4 files changed, 14 insertions(+), 15 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 50044b436..1b45fe9ec 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 @@ -56,12 +56,6 @@ abstract class BasePluginTest { lateinit var artifactBJar: Path private set - val shadowJarPath = ":$SHADOW_JAR_TASK_NAME" - val serverShadowJarPath = ":server:$SHADOW_JAR_TASK_NAME" - val runShadowPath = ":$SHADOW_RUN_TASK_NAME" - val installShadowDistPath = ":$SHADOW_INSTALL_TASK_NAME" - val shadowDistZipPath = ":shadowDistZip" - val projectScript: Path get() = path("build.gradle") val settingsScript: Path get() = path("settings.gradle") open val outputShadowedJar: JarPath get() = jarPath("build/libs/my-1.0-all.jar") @@ -411,6 +405,12 @@ abstract class BasePluginTest { Path(gradleUserHome, "testkit") } + const val shadowJarPath = ":$SHADOW_JAR_TASK_NAME" + const val serverShadowJarPath = ":server:$SHADOW_JAR_TASK_NAME" + const val runShadowPath = ":$SHADOW_RUN_TASK_NAME" + const val installShadowDistPath = ":$SHADOW_INSTALL_TASK_NAME" + const val shadowDistZipPath = ":shadowDistZip" + val entriesInA = arrayOf("a.properties", "a2.properties") val entriesInB = arrayOf("b.properties") val entriesInAB = entriesInA + entriesInB @@ -439,11 +439,10 @@ abstract class BasePluginTest { "-Dorg.gradle.configuration-cache.parallel=true", ) - const val INFO_ARGUMENT = "--info" - // TODO: enable this flag for all tests once we have fixed all issues with isolated projects. // See https://github.com/GradleUp/shadow/pull/1139. - const val IP_ARGUMENT = "-Dorg.gradle.unsafe.isolated-projects=true" + const val ipArgument = "-Dorg.gradle.unsafe.isolated-projects=true" + const val infoArgument = "--info" fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index de3ec62f4..06742d4b2 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -849,10 +849,10 @@ class JavaPluginsTest : BasePluginTest() { val result = run( serverShadowJarPath, - IP_ARGUMENT, + ipArgument, "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a file instead. - INFO_ARGUMENT, + infoArgument, ) assertThat(result.output).all { @@ -883,7 +883,7 @@ class JavaPluginsTest : BasePluginTest() { val result = if (enable) { runWithFailure(shadowJarPath) } else { - run(shadowJarPath, INFO_ARGUMENT) + run(shadowJarPath, infoArgument) } assertThat(result.output).contains( @@ -910,7 +910,7 @@ class JavaPluginsTest : BasePluginTest() { val result = if (enable) { runWithFailure(shadowJarPath, "--fail-on-duplicate-entries") } else { - run(shadowJarPath, INFO_ARGUMENT) + run(shadowJarPath, infoArgument) } assertThat(result.output).contains( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 09e00af32..7c875c1c9 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -260,7 +260,7 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = runWithFailure(shadowJarPath, INFO_ARGUMENT) + val result = runWithFailure(shadowJarPath, infoArgument) assertThat(result.output).contains( "$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: $jvmTargetName", // Logged from Shadow. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 809001652..66ac381f3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -53,7 +53,7 @@ class RelocationTest : BasePluginTest() { } }.toTypedArray() - val result = run(shadowJarPath, INFO_ARGUMENT) + val result = run(shadowJarPath, infoArgument) assertThat(outputShadowedJar).useAll { containsOnly( From 9d38642731840b9e35f6878cfe78279137f73f11 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 13 Aug 2025 12:51:05 +0800 Subject: [PATCH 664/941] Run build tasks with stacktrace on CI (#1645) Easier to capture more details about ``` * What went wrong: Execution failed for task ':lintReportJvm'. > A failure occurred while executing com.android.build.gradle.internal.lint.AndroidLintTask$AndroidLintLauncherWorkAction > There was a failure while executing work items > A failure occurred while executing com.android.build.gradle.internal.lint.AndroidLintWorkAction ``` on CI. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a80bff77e..55d8a9c71 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,7 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java }} - uses: gradle/actions/setup-gradle@v4 - - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" + - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" --stacktrace # Status check that is required in branch protection rules. build-status: From 72c3d4b7e5b642296ff14f3be7751070209aa5fd Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 13 Aug 2025 15:02:33 +0800 Subject: [PATCH 665/941] Tweak local published JARs related tests (#1647) * Update `relocateResourceFiles` * Update `doFirst` * Update `excludeSomeMetaInfFilesByDefault` * Update `publishArtifactCD` * Update `applyTransformersToProjectResources` * Update `includeJavaLibraryConfigurationsByDefault` * Update `jarModule` and `bomModule` * Update `excludeSomeResourcesByDefault` * Fix * Update `includeJavaLibraryConfigurationsByDefault` --- .../gradle/plugins/shadow/BasePluginTest.kt | 60 +++++++++------- .../gradle/plugins/shadow/JavaPluginsTest.kt | 70 ++++++++----------- .../gradle/plugins/shadow/RelocationTest.kt | 17 ++--- .../ServiceFileTransformerTest.kt | 12 ++-- .../shadow/util/AppendableMavenRepository.kt | 4 +- 5 files changed, 75 insertions(+), 88 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 1b45fe9ec..d17b25cd0 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 @@ -66,23 +66,30 @@ abstract class BasePluginTest { localRepo = AppendableMavenRepository( createTempDirectory().resolve("local-maven-repo").createDirectories(), runner(projectDir = null), - ) - localRepo.jarModule("junit", "junit", "3.8.2") { - useJar(junitJar) - }.jarModule("my", "a", "1.0") { - buildJar { - insert("a.properties", "a") - insert("a2.properties", "a2") + ).apply { + jarModule("junit", "junit", "3.8.2") { + useJar(junitJar) } - }.jarModule("my", "b", "1.0") { - buildJar { - insert("b.properties", "b") + jarModule("my", "a", "1.0") { + buildJar { + insert("a.properties", "a") + insert("a2.properties", "a2") + } } - }.publish() - localRepo.bomModule("my", "bom", "1.0") { - addDependency("my", "a", "1.0") - addDependency("my", "b", "1.0") - }.publish() + jarModule("my", "b", "1.0") { + buildJar { + insert("b.properties", "b") + } + } + } + localRepo.publish() + localRepo.apply { + bomModule("my", "bom", "1.0") { + addDependency("my", "a", "1.0") + addDependency("my", "b", "1.0") + } + } + localRepo.publish() artifactAJar = path("my/a/1.0/a-1.0.jar", parent = localRepo.root) artifactBJar = path("my/b/1.0/b-1.0.jar", parent = localRepo.root) @@ -184,18 +191,21 @@ abstract class BasePluginTest { } fun publishArtifactCD(circular: Boolean = false) { - localRepo.jarModule("my", "c", "1.0") { - buildJar { - insert("c.properties", "c") - } - if (circular) { - addDependency("my", "d", "1.0") + localRepo.apply { + jarModule("my", "c", "1.0") { + buildJar { + insert("c.properties", "c") + } + if (circular) { + addDependency("my", "d", "1.0") + } } - }.jarModule("my", "d", "1.0") { - buildJar { - insert("d.properties", "d") + jarModule("my", "d", "1.0") { + buildJar { + insert("d.properties", "d") + } + addDependency("my", "c", "1.0") } - addDependency("my", "c", "1.0") }.publish() } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 06742d4b2..5e519f6f2 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -270,31 +270,22 @@ class JavaPluginsTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/729", ) @Test - fun excludeSomeMetaInfFilesByDefault() { - localRepo.jarModule("my", "a", "1.0") { - buildJar { - insert("a.properties", "a") - insert("META-INF/INDEX.LIST", "JarIndex-Version: 1.0") - insert("META-INF/a.SF", "Signature File") - insert("META-INF/a.DSA", "DSA Signature Block") - insert("META-INF/a.RSA", "RSA Signature Block") - insert("META-INF/a.properties", "key=value") - insert("META-INF/versions/9/module-info.class", "module myModuleName {}") - insert("META-INF/versions/16/module-info.class", "module myModuleName {}") - insert("module-info.class", "module myModuleName {}") - } - }.publish() + fun excludeSomeResourcesByDefault() { + val resJar = buildJar("meta-inf.jar") { + insert("META-INF/INDEX.LIST", "JarIndex-Version: 1.0") + insert("META-INF/a.SF", "Signature File") + insert("META-INF/a.DSA", "DSA Signature Block") + insert("META-INF/a.RSA", "RSA Signature Block") + insert("META-INF/a.properties", "key=value") + insert("META-INF/versions/9/module-info.class", "module myModuleName {}") + insert("META-INF/versions/16/module-info.class", "module myModuleName {}") + insert("module-info.class", "module myModuleName {}") + } - path("src/main/java/my/Passed.java").writeText( - """ - package my; - public class Passed {} - """.trimIndent(), - ) projectScript.appendText( """ dependencies { - implementation 'my:a:1.0' + ${implementationFiles(resJar)} } """.trimIndent(), ) @@ -303,9 +294,6 @@ class JavaPluginsTest : BasePluginTest() { assertThat(outputShadowedJar).useAll { containsOnly( - "my/", - "my/Passed.class", - "a.properties", "META-INF/a.properties", *manifestEntries, ) @@ -336,22 +324,22 @@ class JavaPluginsTest : BasePluginTest() { @Test fun includeJavaLibraryConfigurationsByDefault() { - localRepo.jarModule("my", "api", "1.0") { - buildJar { - insert("api.properties", "api") - } - }.jarModule("my", "implementation-dep", "1.0") { - buildJar { - insert("implementation-dep.properties", "implementation-dep") + localRepo.apply { + jarModule("my", "api", "1.0") { + buildJar { + insert("api.properties", "api") + } } - }.jarModule("my", "implementation", "1.0") { - buildJar { - insert("implementation.properties", "implementation") + jarModule("my", "implementation", "1.0") { + buildJar { + insert("implementation.properties", "implementation") + } + addDependency("my", "b", "1.0") } - addDependency("my", "implementation-dep", "1.0") - }.jarModule("my", "runtimeOnly", "1.0") { - buildJar { - insert("runtimeOnly.properties", "runtimeOnly") + jarModule("my", "runtime-only", "1.0") { + buildJar { + insert("runtime-only.properties", "runtime-only") + } } }.publish() @@ -361,7 +349,7 @@ class JavaPluginsTest : BasePluginTest() { dependencies { api 'my:api:1.0' implementation 'my:implementation:1.0' - runtimeOnly 'my:runtimeOnly:1.0' + runtimeOnly 'my:runtime-only:1.0' } """.trimIndent(), ) @@ -372,8 +360,8 @@ class JavaPluginsTest : BasePluginTest() { containsOnly( "api.properties", "implementation.properties", - "runtimeOnly.properties", - "implementation-dep.properties", + "runtime-only.properties", + *entriesInB, *manifestEntries, ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 66ac381f3..a1ef73919 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -284,23 +284,16 @@ class RelocationTest : BasePluginTest() { ) @Test fun relocateResourceFiles() { - localRepo.jarModule("my", "dep", "1.0") { - buildJar { - insert("foo/dep.properties", "c") - } - }.publish() - path("src/main/java/foo/Foo.java").writeText( - """ - package foo; - class Foo {} - """.trimIndent(), - ) + val depJar = buildJar("foo.jar") { + insert("foo/dep.properties", "c") + } + writeClass(packageName = "foo", className = "Foo") path("src/main/resources/foo/foo.properties").writeText("name=foo") projectScript.appendText( """ dependencies { - implementation 'my:dep:1.0' + ${implementationFiles(depJar)} } $shadowJarTask { relocate 'foo', 'bar' diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 9e3db1b67..1f0f47580 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -169,17 +169,13 @@ class ServiceFileTransformerTest : BaseTransformerTest() { val one = buildJarOne { insert(servicesBarEntry, CONTENT_ONE) } - localRepo.jarModule("foo", "bar", "1.0") { - buildJar { - insert(servicesBarEntry, CONTENT_TWO) - } - }.publish() - + val two = buildJarTwo { + insert(servicesBarEntry, CONTENT_TWO) + } projectScript.appendText( """ dependencies { - implementation 'foo:bar:1.0' - ${implementationFiles(one)} + ${implementationFiles(one, two)} } $shadowJarTask { mergeServiceFiles() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index 90661f740..3512e12b2 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -34,7 +34,7 @@ class AppendableMavenRepository( artifactId: String, version: String, action: JarModule.() -> Unit, - ) = apply { + ) { modules += JarModule(groupId, artifactId, version).also(action) } @@ -43,7 +43,7 @@ class AppendableMavenRepository( artifactId: String, version: String, action: BomModule.() -> Unit, - ) = apply { + ) { modules += BomModule(groupId, artifactId, version).also(action) } From 7f9f43ae4161a71b33ab233412860b4c3c489149 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 13 Aug 2025 15:12:21 +0800 Subject: [PATCH 666/941] Enable ignoreTestFixturesSources for lints (#1648) --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index a1b456919..cd8629722 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,6 +48,7 @@ kotlin { lint { baseline = file("gradle/lint-baseline.xml") + ignoreTestFixturesSources = true ignoreTestSources = true warningsAsErrors = true } From f224654e8cb82aadbfc00d155f68072cced5c0b5 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 13 Aug 2025 15:17:10 +0800 Subject: [PATCH 667/941] Remove Gradle 8.11 on windows-11-arm --- .github/workflows/build.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 55d8a9c71..ff5a8eb9a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,9 +23,6 @@ jobs: java: 24 include: # We just test one JDK version on Windows. - - os: windows-11-arm - gradle: 8.11 - java: 21 - os: windows-11-arm gradle: current java: 21 From da9c615867eaef9e703d220f70813ba30450d5d1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 13 Aug 2025 15:54:13 +0800 Subject: [PATCH 668/941] Update build-status check (#1651) --- .github/workflows/build.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ff5a8eb9a..20884f92e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -40,9 +40,15 @@ jobs: build-status: needs: build runs-on: ubuntu-24.04-arm + if: always() steps: - - run: | - echo all build jobs completed successfully. + - name: Check build matrix result + run: | + if [ "${{ needs.build.result }}" != "success" ]; then + echo "Some build jobs failed." + exit 1 + fi + echo "All build jobs completed successfully." publish-snapshot: needs: build From 53cd662ab2ca8a6341b8628568347a9a4444fefb Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 13 Aug 2025 16:08:08 +0800 Subject: [PATCH 669/941] Revert "Remove Gradle 8.11 on windows-11-arm" This reverts commit f224654e8cb82aadbfc00d155f68072cced5c0b5. --- .github/workflows/build.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 20884f92e..2d95d8b36 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,6 +23,9 @@ jobs: java: 24 include: # We just test one JDK version on Windows. + - os: windows-11-arm + gradle: 8.11 + java: 21 - os: windows-11-arm gradle: current java: 21 From 8ab59bafa0f3f520ec8826bb89f026776596ca1e Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 13 Aug 2025 16:08:30 +0800 Subject: [PATCH 670/941] Fallback to windows-latest --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2d95d8b36..f4bbe44f7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,10 +23,10 @@ jobs: java: 24 include: # We just test one JDK version on Windows. - - os: windows-11-arm + - os: windows-latest gradle: 8.11 java: 21 - - os: windows-11-arm + - os: windows-latest gradle: current java: 21 runs-on: ${{ matrix.os }} From 8eedce88ca8b4a72e336d63cc7cf9c2cec144bb7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 13 Aug 2025 19:04:18 +0800 Subject: [PATCH 671/941] Support publishing all modules at once (#1646) * Merge logic in `AppendableMavenRepository` * Update `BasePluginTest` * Fix * Delete the root * Revert "Delete the root" This reverts commit 44b3b8e3734777825a1450a6765df198a45f589e. * Update `publishArtifactCD` * Publish circular e and f * Comment * Type-safe Coordinate * Simplify * Remove addDependency overload * Update src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Cleanup --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../gradle/plugins/shadow/BasePluginTest.kt | 60 ++++++------ .../gradle/plugins/shadow/CachingTest.kt | 1 - .../gradle/plugins/shadow/FilteringTest.kt | 60 ++++++------ .../gradle/plugins/shadow/JavaPluginsTest.kt | 2 +- .../gradle/plugins/shadow/MinimizeTest.kt | 8 +- .../shadow/util/AppendableMavenRepository.kt | 92 ++++++++++++------- 6 files changed, 130 insertions(+), 93 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 d17b25cd0..97c11a3da 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 @@ -70,23 +70,50 @@ abstract class BasePluginTest { jarModule("junit", "junit", "3.8.2") { useJar(junitJar) } - jarModule("my", "a", "1.0") { + val a = jarModule("my", "a", "1.0") { buildJar { insert("a.properties", "a") insert("a2.properties", "a2") } } - jarModule("my", "b", "1.0") { + val b = jarModule("my", "b", "1.0") { buildJar { insert("b.properties", "b") } } - } - localRepo.publish() - localRepo.apply { + val c = jarModule("my", "c", "1.0") { + buildJar { + insert("c.properties", "c") + } + } + val d = jarModule("my", "d", "1.0") { + buildJar { + insert("d.properties", "d") + } + // Depends on c but c does not depend on d. + addDependency(c) + } + val e = jarModule("my", "e", "1.0") { + buildJar { + insert("e.properties", "e") + } + // Circular dependency with f. + addDependency("my:f:1.0") + } + val f = jarModule("my", "f", "1.0") { + buildJar { + insert("f.properties", "f") + } + // Circular dependency with e. + addDependency(e) + } bomModule("my", "bom", "1.0") { - addDependency("my", "a", "1.0") - addDependency("my", "b", "1.0") + addDependency(a) + addDependency(b) + addDependency(c) + addDependency(d) + addDependency(e) + addDependency(f) } } localRepo.publish() @@ -190,25 +217,6 @@ abstract class BasePluginTest { .buildAndFail().assertNoDeprecationWarnings() } - fun publishArtifactCD(circular: Boolean = false) { - localRepo.apply { - jarModule("my", "c", "1.0") { - buildJar { - insert("c.properties", "c") - } - if (circular) { - addDependency("my", "d", "1.0") - } - } - jarModule("my", "d", "1.0") { - buildJar { - insert("d.properties", "d") - } - addDependency("my", "c", "1.0") - } - }.publish() - } - fun writeClass( sourceSet: String = "main", packageName: String = "my", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt index 12b85a63a..cf651cae2 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -95,7 +95,6 @@ class CachingTest : BasePluginTest() { @Test fun dependencyFilterChanged() { - publishArtifactCD() projectScript.appendText( """ dependencies { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index cdadc8ec7..2cd2b316b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -4,7 +4,6 @@ import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText -import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest @@ -12,12 +11,6 @@ import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.ValueSource class FilteringTest : BasePluginTest() { - @BeforeAll - override fun doFirst() { - super.doFirst() - publishArtifactCD() - } - @BeforeEach override fun setup() { super.setup() @@ -67,7 +60,28 @@ class FilteringTest : BasePluginTest() { @ParameterizedTest @ValueSource(booleans = [false, true]) fun excludeDependency(useAccessor: Boolean) { - dependOnAndExcludeArtifactD(useAccessor) + settingsScript.appendText( + """ + dependencyResolutionManagement { + versionCatalogs.create('libs') { + library('my-d', 'my:d:1.0') + } + } + """.trimIndent(), + ) + val dependency = if (useAccessor) "libs.my.d" else "'my:d:1.0'" + projectScript.appendText( + """ + dependencies { + implementation $dependency + } + $shadowJarTask { + dependencies { + exclude(dependency($dependency)) + } + } + """.trimIndent(), + ) run(shadowJarPath) @@ -200,25 +214,7 @@ class FilteringTest : BasePluginTest() { @Test fun handleExcludeWithCircularDependency() { - publishArtifactCD(circular = true) - dependOnAndExcludeArtifactD() - - run(shadowJarPath) - - commonAssertions() - } - - private fun dependOnAndExcludeArtifactD(useAccessor: Boolean = false) { - settingsScript.appendText( - """ - dependencyResolutionManagement { - versionCatalogs.create('libs') { - library('my-d', 'my:d:1.0') - } - } - """.trimIndent(), - ) - val dependency = if (useAccessor) "libs.my.d" else "'my:d:1.0'" + val dependency = "'my:e:1.0'" projectScript.appendText( """ dependencies { @@ -231,6 +227,16 @@ class FilteringTest : BasePluginTest() { } """.trimIndent(), ) + + run(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + containsOnly( + "f.properties", + *entriesInAB, + *manifestEntries, + ) + } } private fun commonAssertions() { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 5e519f6f2..6aaf411b3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -334,7 +334,7 @@ class JavaPluginsTest : BasePluginTest() { buildJar { insert("implementation.properties", "implementation") } - addDependency("my", "b", "1.0") + addDependency("my:b:1.0") } jarModule("my", "runtime-only", "1.0") { buildJar { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index 38bdbc420..497d165ed 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -219,9 +219,7 @@ class MinimizeTest : BasePluginTest() { ) @Test fun excludeCircularDependencies() { - publishArtifactCD(circular = true) - - val dependency = "'my:d:1.0'" + val dependency = "'my:e:1.0'" projectScript.appendText( """ dependencies { @@ -239,8 +237,8 @@ class MinimizeTest : BasePluginTest() { assertThat(outputShadowedJar).useAll { containsOnly( - "c.properties", - "d.properties", + "e.properties", + "f.properties", *manifestEntries, ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index 3512e12b2..6d5e45d34 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -2,8 +2,10 @@ package com.github.jengelman.gradle.plugins.shadow.util import com.github.jengelman.gradle.plugins.shadow.BasePluginTest.Companion.commonArguments import java.nio.file.Path +import kotlin.io.path.appendText import kotlin.io.path.createDirectory import kotlin.io.path.createFile +import kotlin.io.path.createParentDirectories import kotlin.io.path.exists import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.isRegularFile @@ -11,6 +13,7 @@ import kotlin.io.path.name import kotlin.io.path.writeText import org.apache.maven.model.Dependency import org.apache.maven.model.Model +import org.gradle.api.logging.Logging import org.gradle.testkit.runner.GradleRunner class AppendableMavenRepository( @@ -24,7 +27,7 @@ class AppendableMavenRepository( check(root.exists()) { "Maven repository root directory does not exist: $root" } root.resolve("settings.gradle").createFile() - .writeText("rootProject.name = '${root.name}'") + .writeText("rootProject.name = '${root.name}'$lineSeparator") root.resolve("build.gradle").createFile() jarsDir = root.resolve("jars").createDirectory() } @@ -34,8 +37,10 @@ class AppendableMavenRepository( artifactId: String, version: String, action: JarModule.() -> Unit, - ) { - modules += JarModule(groupId, artifactId, version).also(action) + ): String { + val jarModule = JarModule(groupId, artifactId, version).also(action) + modules += jarModule + return jarModule.coordinate } fun bomModule( @@ -43,32 +48,42 @@ class AppendableMavenRepository( artifactId: String, version: String, action: BomModule.() -> Unit, - ) { - modules += BomModule(groupId, artifactId, version).also(action) + ): String { + val bomModule = BomModule(groupId, artifactId, version).also(action) + modules += bomModule + return bomModule.coordinate } fun publish() { check(modules.isNotEmpty()) { "No modules to publish. Please add at least one module." } - val groups = modules.groupBy { it::class }.entries - check(groups.size == 1) { - "Only one type of module can be published at a time." - } - - @Suppress("UNCHECKED_CAST") - when (val type = groups.first().key) { - JarModule::class -> { - publishJarModules(modules as List) - } - BomModule::class -> { - publishBomModules(modules as List) + modules.groupBy { it::class }.forEach { (type, group) -> + @Suppress("UNCHECKED_CAST") + when (type) { + JarModule::class -> { + configureJarModules(group as List) + } + BomModule::class -> { + configureBomModules(group as List) + } + else -> error("Unsupported module type: $type") } - else -> error("Unsupported module type: $type") } + + gradleRunner.withProjectDir(root.toFile()) + .withArguments(commonArguments + "publish") + .build() + logger.info( + """ + Publish modules to Maven repository at ${root.toUri()}: + ${modules.joinToString(lineSeparator) { it.coordinate }} + """.trimIndent(), + ) + modules.clear() } - private fun publishJarModules(jarModules: List) { + private fun configureJarModules(jarModules: List) { val mavenPublications = jarModules.joinToString(lineSeparator) { module -> var index = -1 val nodes = module.dependencies.joinToString(lineSeparator) { @@ -105,13 +120,16 @@ class AppendableMavenRepository( } } """.trimIndent() - runPublish(scriptContent) - modules.clear() + val jarsModule = "jars-module" + root.resolve("settings.gradle").appendText("include '$jarsModule'$lineSeparator") + root.resolve("$jarsModule/build.gradle") + .createFileIfNotExists() + .writeText(scriptContent) } - private fun publishBomModules(bomModules: List) { + private fun configureBomModules(bomModules: List) { // BOM modules are published one by one. - bomModules.forEach { module -> + bomModules.forEachIndexed { index, module -> val scriptContent = """ plugins { id 'maven-publish' @@ -131,9 +149,12 @@ class AppendableMavenRepository( } } """.trimIndent() - runPublish(scriptContent) + val pomModule = "pom-module-$index" + root.resolve("settings.gradle").appendText("include '$pomModule'$lineSeparator") + root.resolve("$pomModule/build.gradle") + .createFileIfNotExists() + .writeText(scriptContent) } - modules.clear() } private fun Module.createMavenPublication( @@ -149,13 +170,6 @@ class AppendableMavenRepository( """.trimIndent() } - private fun runPublish(scriptContent: String) { - root.resolve("build.gradle").writeText(scriptContent) - gradleRunner.withProjectDir(root.toFile()) - .withArguments(commonArguments + "publish") - .build() - } - sealed class Module( groupId: String, artifactId: String, @@ -169,7 +183,9 @@ class AppendableMavenRepository( this.version = version } - fun addDependency(groupId: String, artifactId: String, version: String, scope: String = "runtime") { + fun addDependency(coordinate: String, scope: String = "runtime") { + val (groupId, artifactId, version) = coordinate.split(":").takeIf { it.size == 3 } + ?: error("Invalid coordinate format: '$coordinate'. Expected format is 'groupId:artifactId:version'.") val dependency = Dependency().also { it.groupId = groupId it.artifactId = artifactId @@ -213,6 +229,16 @@ class AppendableMavenRepository( } } +private val logger = Logging.getLogger(AppendableMavenRepository::class.java) + private val lineSeparator = System.lineSeparator() val Dependency.coordinate: String get() = "$groupId:$artifactId:$version" + +private fun Path.createFileIfNotExists(): Path { + if (!exists()) { + createParentDirectories() + createFile() + } + return this +} From aabf6526b3c062440cc3df7a531cc36ccf3afbbf Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 13 Aug 2025 19:39:52 +0800 Subject: [PATCH 672/941] Remove override of maxParallelForks (#1652) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This doesn’t seem to help much with speeding things up... --- build.gradle.kts | 2 -- 1 file changed, 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index cd8629722..65188a1d2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -182,8 +182,6 @@ testing.suites { targets.configureEach { testTask { systemProperty("TEST_GRADLE_VERSION", testGradleVersion) - maxParallelForks = Runtime.getRuntime().availableProcessors() - develocity { testRetry { maxRetries = 2 From cc521b7bc00a9702dac0d2f4ce60a1092730a589 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 13 Aug 2025 21:27:34 +0800 Subject: [PATCH 673/941] Log testGradleVersion at lifecycle level --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 65188a1d2..0fbc62846 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -99,7 +99,7 @@ publishing.publications.withType().configureEach { val testGradleVersion: String = providers.gradleProperty("testGradleVersion").orNull.let { val value = if (it == null || it == "current") GradleVersion.current().version else it - logger.info("Using test Gradle version: $value") + logger.lifecycle("Using test Gradle version: $value") value } From 55d04732cd820939c403246c7fa47d7f560c09a2 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 13 Aug 2025 21:31:26 +0800 Subject: [PATCH 674/941] Revert "Add testGradleVersion into buildScan values (#1521)" This reverts commit 4281b730 --- build.gradle.kts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0fbc62846..2e9f9111a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -103,10 +103,6 @@ val testGradleVersion: String = providers.gradleProperty("testGradleVersion").or value } -develocity { - buildScan.value("testGradleVersion", testGradleVersion) -} - dependencies { compileOnly(libs.develocity) compileOnly(libs.kotlin.kmp) From de510adc9b9cc54813d2ee9a5a4983d11c241baf Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 13 Aug 2025 21:53:43 +0800 Subject: [PATCH 675/941] Update lint baseline --- gradle/lint-baseline.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index d0297cb3f..1789842c8 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -151,7 +151,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -162,7 +162,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -173,7 +173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,7 +184,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> From d336a978b797db5c3aaf53e38f5a5b3edca5931d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 13 Aug 2025 22:07:14 +0800 Subject: [PATCH 676/941] Assert build info like "will fail with an error in Gradle" (#1654) --- .../com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt | 1 + 1 file changed, 1 insertion(+) 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 97c11a3da..cabab8ea2 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 @@ -488,6 +488,7 @@ abstract class BasePluginTest { assertThat(output).doesNotContain( "has been deprecated and is scheduled to be removed in Gradle", "has been deprecated. This is scheduled to be removed in Gradle", + "will fail with an error in Gradle", ) } From a4c14ba2e3aa0ff9ccb862f8d8f94bc94ad04f40 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 14 Aug 2025 00:09:32 +0800 Subject: [PATCH 677/941] Gradle 9.1.0-rc-1 (#1653) * https://docs.gradle.org/9.1.0-rc-1/release-notes.html * Update `doNotErrorOnRelocatingJava9Classes` * Disable `--warning-mode=fail` for KMP https://youtrack.jetbrains.com/issue/KT-78620 * Remove AGP and `AndroidPluginTest` --- .github/renovate.json5 | 7 ----- build.gradle.kts | 1 - gradle/libs.versions.toml | 3 -- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 3 -- gradlew.bat | 3 +- .../shadow/snippet/SnippetExecutable.kt | 11 ++++++- .../plugins/shadow/AndroidPluginTest.kt | 29 ------------------- .../gradle/plugins/shadow/BasePluginTest.kt | 17 +++++++++-- .../gradle/plugins/shadow/RelocationTest.kt | 6 ++-- 10 files changed, 30 insertions(+), 52 deletions(-) delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 5b51e7a02..62b033c77 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -15,12 +15,5 @@ "org.ow2.asm:asm-commons" ], }, - { - // AGP version should match the min Gradle version used in tests. - "enabled": false, - "matchPackageNames": [ - 'com.android.tools.build:gradle', - ], - }, ] } diff --git a/build.gradle.kts b/build.gradle.kts index 2e9f9111a..faeead404 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -115,7 +115,6 @@ dependencies { implementation(libs.plexus.utils) implementation(libs.plexus.xml) - testPluginClasspath(libs.agp) testPluginClasspath(libs.foojayResolver) testPluginClasspath(libs.develocity) testPluginClasspath(libs.kotlin.kmp) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a39426822..a41b392a8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,9 +22,6 @@ foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.to develocity = "com.gradle:develocity-gradle-plugin:4.1" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } -# AGP version should match the min Gradle version used in tests. -# https://developer.android.com/build/releases/gradle-plugin#updating-gradle -agp = "com.android.tools.build:gradle:8.8.0" androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2a84e188b..f5651b719 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-rc-1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index ef07e0162..adff685a0 100755 --- a/gradlew +++ b/gradlew @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac20..c4bdd3ab8 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index d684c52a5..8a8b2fb95 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -82,13 +82,22 @@ sealed class SnippetExecutable : Executable { JarOutputStream(it.outputStream()).use {} } + val warningMode = if (mainScript.contains("org.jetbrains.kotlin.multiplatform")) { + "none" // TODO: https://youtrack.jetbrains.com/issue/KT-78620 + } else { + "fail" + } + try { GradleRunner.create() .withGradleVersion(testGradleVersion) .withProjectDir(projectRoot.toFile()) .withPluginClasspath() .forwardOutput() - .withArguments("--warning-mode=fail", "build") + .withArguments( + "--warning-mode=$warningMode", + "build", + ) .build() } catch (t: Throwable) { throw RuntimeException("Failed to execute snippet:\n\n$mainScript", t) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt deleted file mode 100644 index 0357eeed0..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/AndroidPluginTest.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import assertk.assertThat -import assertk.assertions.contains -import kotlin.io.path.writeText -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.MethodSource - -class AndroidPluginTest : BasePluginTest() { - @ParameterizedTest - @MethodSource("androidIdsProvider") - fun doNotCompatAgp(pluginId: String) { - projectScript.writeText(getDefaultProjectBuildScript(plugin = pluginId)) - - assertThat(runWithFailure().output).contains( - "Shadow does not support using with AGP, you may need Android Fused Library plugin instead.", - ) - } - - private companion object { - @JvmStatic - fun androidIdsProvider() = listOf( - "com.android.application", - "com.android.library", - "com.android.test", - "com.android.dynamic-feature", - ) - } -} 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 cabab8ea2..ae9f0b4ff 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 @@ -400,7 +400,21 @@ abstract class BasePluginTest { .forwardOutput() .withPluginClasspath() .withTestKitDir(testKitDir.toFile()) - .withArguments(commonArguments + arguments) + .withArguments( + buildList { + val warningsAsErrors = try { + // TODO: https://youtrack.jetbrains.com/issue/KT-78620 + !projectScript.readText().contains("org.jetbrains.kotlin.multiplatform") + } catch (_: UninitializedPropertyAccessException) { + true // Default warning mode if projectScript is not initialized yet. + } + if (warningsAsErrors) { + add("--warning-mode=fail") + } + addAll(commonArguments) + addAll(arguments) + }, + ) .apply { if (projectDir != null) { withProjectDir(projectDir.toFile()) @@ -448,7 +462,6 @@ abstract class BasePluginTest { const val jarTask = "tasks.named('jar', Jar)" val commonArguments = listOf( - "--warning-mode=fail", "--configuration-cache", "--build-cache", "--parallel", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index a1ef73919..ed66524a0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -323,9 +323,9 @@ class RelocationTest : BasePluginTest() { """ dependencies { implementation 'org.slf4j:slf4j-api:1.7.21' - implementation group: 'io.netty', name: 'netty-all', version: '4.0.23.Final' - implementation group: 'com.google.protobuf', name: 'protobuf-java', version: '2.5.0' - implementation group: 'org.apache.zookeeper', name: 'zookeeper', version: '3.4.6' + implementation 'io.netty:netty-all:4.0.23.Final' + implementation 'com.google.protobuf:protobuf-java:2.5.0' + implementation 'org.apache.zookeeper:zookeeper:3.4.6' } $shadowJarTask { zip64 = true From 61e72e99e50e5ba06c003ead77fc2319aaa4fb68 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 14 Aug 2025 00:27:01 +0800 Subject: [PATCH 678/941] Remove outdated test for issue 294 (#1656) Reverts 830cbb23b77514a51bc26ce950876cb0fc42db57. --- .../gradle/plugins/shadow/RelocationTest.kt | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index ed66524a0..9fc730aee 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -314,40 +314,6 @@ class RelocationTest : BasePluginTest() { } } - @Issue( - "https://github.com/GradleUp/shadow/issues/294", - ) - @Test - fun doNotErrorOnRelocatingJava9Classes() { - projectScript.appendText( - """ - dependencies { - implementation 'org.slf4j:slf4j-api:1.7.21' - implementation 'io.netty:netty-all:4.0.23.Final' - implementation 'com.google.protobuf:protobuf-java:2.5.0' - implementation 'org.apache.zookeeper:zookeeper:3.4.6' - } - $shadowJarTask { - zip64 = true - relocate 'com.google.protobuf', 'shaded.com.google.protobuf' - relocate 'io.netty', 'shaded.io.netty' - } - """.trimIndent(), - ) - - run(shadowJarPath) - - val entries = outputShadowedJar.use { it.entries().toList() } - val included = entries.filter { entry -> - entry.name.startsWith("shaded/com/google/protobuf") || entry.name.startsWith("shaded/io/netty") - } - val excluded = entries.filter { entry -> - entry.name.startsWith("com/google/protobuf") || entry.name.startsWith("io/netty") - } - assertThat(included).isNotEmpty() - assertThat(excluded).isEmpty() - } - @ParameterizedTest @MethodSource("preserveLastModifiedProvider") fun preserveLastModifiedCorrectly(enableAutoRelocation: Boolean, preserveFileTimestamps: Boolean) { From cdbf72057c85f33b751b5650d2091ee4e8d3d7b3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 14 Aug 2025 09:21:55 +0800 Subject: [PATCH 679/941] Test using filesNotMatching with DuplicatesStrategy.INCLUDE (#1657) --- .../ServiceFileTransformerTest.kt | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 1f0f47580..67f456800 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -226,7 +226,6 @@ class ServiceFileTransformerTest : BaseTransformerTest() { projectScript.appendText( """ $shadowJarTask { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE filesMatching('$ENTRY_SERVICES_SHADE') { duplicatesStrategy = DuplicatesStrategy.INCLUDE } @@ -242,6 +241,27 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } + @Test + fun strategyIncludeCanBeOverriddenByFilesNotMatching() { + writeDuplicatesStrategy(DuplicatesStrategy.INCLUDE) + projectScript.appendText( + """ + $shadowJarTask { + filesNotMatching('$ENTRY_SERVICES_SHADE') { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + } + } + """.trimIndent(), + ) + + run(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) + getContent(ENTRY_SERVICES_FOO).isEqualTo("one") + } + } + private fun writeDuplicatesStrategy(strategy: DuplicatesStrategy) { projectScript.appendText( """ From 9de3b215fb113ddc3e54508d81d5648ff65104b1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 14 Aug 2025 10:56:58 +0800 Subject: [PATCH 680/941] Add explicit lint workflow (#1659) --- .github/workflows/build.yml | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f4bbe44f7..eee949f61 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -37,7 +37,21 @@ jobs: distribution: 'zulu' java-version: ${{ matrix.java }} - uses: gradle/actions/setup-gradle@v4 - - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" --stacktrace + - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" --stacktrace -x lint + + # TODO: https://issuetracker.google.com/issues/438361087 + lint: + runs-on: ubuntu-24.04-arm + steps: + - uses: actions/checkout@v5 + - uses: actions/setup-java@v4 + with: + distribution: 'zulu' + java-version: 21 + - uses: gradle/actions/setup-gradle@v4 + with: + cache-read-only: true + - run: ./gradlew lint # Status check that is required in branch protection rules. build-status: From e03d4f1580d42e530aacf7ccbdd24abb6c90a725 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 14 Aug 2025 11:06:57 +0800 Subject: [PATCH 681/941] Make build-status depend on more jobs (#1658) - https://jakewharton.com/fan-in-to-a-single-required-github-action/ - https://github.com/JakeWharton/mosaic/blob/d4cc955eaadba0d18fcad5749c14a699415a1ae9/.github/workflows/build.yaml#L243-L259 --- .github/workflows/build.yml | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eee949f61..f4b10ced1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,20 +55,23 @@ jobs: # Status check that is required in branch protection rules. build-status: - needs: build + needs: + - build + - lint runs-on: ubuntu-24.04-arm if: always() steps: - - name: Check build matrix result + - name: Check run: | - if [ "${{ needs.build.result }}" != "success" ]; then - echo "Some build jobs failed." + results=$(tr -d '\n' <<< '${{ toJSON(needs.*.result) }}') + if ! grep -q -v -E '(failure|cancelled)' <<< "$results"; then + echo "One or more required jobs failed" exit 1 fi - echo "All build jobs completed successfully." + echo "All required jobs completed successfully." publish-snapshot: - needs: build + needs: build-status runs-on: ubuntu-24.04-arm if: github.event.repository.fork == false && github.ref == 'refs/heads/main' steps: From 04cdee5e2bf6220f182f7b97bebd813909ee3002 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 14 Aug 2025 11:33:45 +0800 Subject: [PATCH 682/941] Back to windows-11-arm --- .github/workflows/build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f4b10ced1..92a97e3b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,10 +23,10 @@ jobs: java: 24 include: # We just test one JDK version on Windows. - - os: windows-latest + - os: windows-11-arm gradle: 8.11 java: 21 - - os: windows-latest + - os: windows-11-arm gradle: current java: 21 runs-on: ${{ matrix.os }} From e88543a0335f2b60787a0f96b02a8dfbad7ce341 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Aug 2025 08:49:53 +0000 Subject: [PATCH 683/941] Update kotlin monorepo to v2.2.10 (#1660) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a41b392a8..686bd6246 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "2.2.0" +kotlin = "2.2.10" moshi = "1.15.2" pluginPublish = "1.3.1" From b7e181871010f816b2fcd49aabbaa517dbb6980e Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 15 Aug 2025 11:43:37 +0800 Subject: [PATCH 684/941] Prepare version 9.0.2 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 7a57af558..8a88bc5fa 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.1...HEAD) - 2025-xx-xx +## [9.0.2](https://github.com/GradleUp/shadow/releases/tag/9.0.2) - 2025-08-15 ### Fixed diff --git a/gradle.properties b/gradle.properties index 38e449b17..34e23765d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.2-SNAPSHOT +VERSION_NAME=9.0.2 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From df218e060b70206626d4625afc80c7369bc1849f Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 15 Aug 2025 11:44:23 +0800 Subject: [PATCH 685/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 8a88bc5fa..bb16585e1 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.2...HEAD) - 2025-xx-xx + + ## [9.0.2](https://github.com/GradleUp/shadow/releases/tag/9.0.2) - 2025-08-15 ### Fixed diff --git a/gradle.properties b/gradle.properties index 34e23765d..1dd6be137 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.2 +VERSION_NAME=9.0.3-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 9bf4bd4fb5bcc055a21ff78042ad6f76715a0cfa Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 15 Aug 2025 12:04:35 +0800 Subject: [PATCH 686/941] Revert "Upload the artifacts" This reverts commit c86a9b33256ced4a9bd8500bb1053ac89a54ebcf. # Conflicts: # .github/workflows/ci.yml --- .github/workflows/build.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 92a97e3b3..75462424a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,7 +89,3 @@ jobs: ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.CENTRAL_PORTAL_PASSWORD }} ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} - - uses: actions/upload-artifact@v4 - with: - path: build/libs - if-no-files-found: 'error' From 019ff7b1695aa5fbba573a10dfb629537d8676bb Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 16 Aug 2025 11:31:47 +0800 Subject: [PATCH 687/941] Tweak wording about testGradleVersion --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index faeead404..54cb1781d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -99,7 +99,7 @@ publishing.publications.withType().configureEach { val testGradleVersion: String = providers.gradleProperty("testGradleVersion").orNull.let { val value = if (it == null || it == "current") GradleVersion.current().version else it - logger.lifecycle("Using test Gradle version: $value") + logger.lifecycle("Using Gradle $value in tests") value } From abaee091e712131fed3a7ca08fdc06da9fdf9f7b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 17 Aug 2025 11:44:05 +0800 Subject: [PATCH 688/941] Tweak functional tests and remove outdated ones (#1663) * Tweak writeGradlePluginModule * Tweak assertions * Update DisabledOnOs * Update CachingTest * Update FilteringTest * Update JavaPluginsTest * Replace @MethodSource * Update PublishingTest * Remove outdated relocateDoesNotDropDependencyResources * Update RelocationTest * Remove outdated shadowManifestLeaksToJarManifest * Update ServiceFileTransformerTest * Imports * Revert --- .../plugins/shadow/ApplicationPluginTest.kt | 13 +- .../gradle/plugins/shadow/BasePluginTest.kt | 26 +-- .../gradle/plugins/shadow/CachingTest.kt | 8 +- .../gradle/plugins/shadow/FilteringTest.kt | 26 ++- .../gradle/plugins/shadow/JavaPluginsTest.kt | 23 +-- .../gradle/plugins/shadow/PublishingTest.kt | 49 ++--- .../gradle/plugins/shadow/RelocationTest.kt | 167 +++++------------- .../ServiceFileTransformerTest.kt | 4 +- .../shadow/transformers/TransformersTest.kt | 20 --- 9 files changed, 96 insertions(+), 240 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 494518998..386311bf0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -37,7 +37,7 @@ class ApplicationPluginTest : BasePluginTest() { @DisabledOnOs( OS.WINDOWS, architectures = ["aarch64"], - disabledReason = "https://github.com/gradle/gradle/issues/29807", + disabledReason = "Cannot use toolchain on Windows ARM64", // TODO: https://github.com/gradle/gradle/issues/29807 ) @Test fun integrationWithApplicationPluginAndJavaToolchains() { @@ -140,6 +140,9 @@ class ApplicationPluginTest : BasePluginTest() { } """.trimIndent(), ) + + var result = run(runShadowPath) + val assertions = { output: String, arg: String -> assertThat(output).all { // Prefer main class from `application.main` over the one in manifest attributes. @@ -147,8 +150,7 @@ class ApplicationPluginTest : BasePluginTest() { doesNotContain("Hello, World! ($arg) from Main2") } } - - assertions(run(runShadowPath).output, "foo") + assertions(result.output, "foo") commonAssertions( jarPath("build/libs/myapp-1.0-all.jar"), entriesContained = entriesInA + arrayOf(mainClass, main2ClassEntry), @@ -162,7 +164,10 @@ class ApplicationPluginTest : BasePluginTest() { } """.trimIndent(), ) - assertions(run(":run").output, "bar") + + result = run(":run") + + assertions(result.output, "bar") } @Test 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 ae9f0b4ff..9daefea28 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 @@ -348,32 +348,18 @@ abstract class BasePluginTest { path("server/build.gradle").writeText(replaced) } - fun writeGradlePluginModule(legacy: Boolean) { - val pluginId = "my.plugin" - val pluginClass = "my.plugin.MyPlugin" - val gradlePluginBlock: String - - if (legacy) { - gradlePluginBlock = "" - path("src/main/resources/META-INF/gradle-plugins/$pluginId.properties") - .writeText("implementation-class=$pluginClass") - } else { - gradlePluginBlock = """ + fun writeGradlePluginModule() { + projectScript.writeText( + """ + ${getDefaultProjectBuildScript("java-gradle-plugin", withGroup = true, withVersion = true)} gradlePlugin { plugins { create('myPlugin') { - id = '$pluginId' - implementationClass = '$pluginClass' + id = 'my.plugin' + implementationClass = 'my.plugin.MyPlugin' } } } - """.trimIndent() - } - - projectScript.writeText( - """ - ${getDefaultProjectBuildScript("java-gradle-plugin", withGroup = true, withVersion = true)} - $gradlePluginBlock """.trimIndent() + lineSeparator, ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt index cf651cae2..d2eba7dd6 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -26,9 +26,6 @@ import org.junit.jupiter.api.Test class CachingTest : BasePluginTest() { private var taskPath: String = shadowJarPath - /** - * Ensure that a basic usage reuses an output from cache and then gets a cache miss when the content changes. - */ @Test fun dependenciesChanged() { projectScript.appendText( @@ -411,11 +408,8 @@ class CachingTest : BasePluginTest() { } } - /** - * Ensure that we get a cache miss when relocation changes and that caching works with relocation - */ @Test - fun relocatorAdded() { + fun relocatorChanged() { projectScript.appendText( """ dependencies { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 2cd2b316b..be0b10090 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -7,7 +7,6 @@ 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.MethodSource import org.junit.jupiter.params.provider.ValueSource class FilteringTest : BasePluginTest() { @@ -89,7 +88,16 @@ class FilteringTest : BasePluginTest() { } @ParameterizedTest - @MethodSource("wildcardDepProvider") + @ValueSource( + strings = [ + "my:d", + "m.*:d", + "my:d:.*", + "m.*:d:.*", + "m.*:d.*:.*", + ".*:d:.*", + ], + ) fun excludeDependencyUsingWildcardSyntax(wildcard: String) { projectScript.appendText( """ @@ -167,7 +175,7 @@ class FilteringTest : BasePluginTest() { } @Test - fun excludeATransitiveProjectDependency() { + fun excludeTransitiveProjectDependency() { writeClientAndServerModules( serverShadowBlock = """ dependencies { @@ -248,16 +256,4 @@ class FilteringTest : BasePluginTest() { ) } } - - private companion object { - @JvmStatic - fun wildcardDepProvider() = listOf( - "my:d", - "m.*:d", - "my:d:.*", - "m.*:d:.*", - "m.*:d.*:.*", - ".*:d:.*", - ) - } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 6aaf411b3..98a25cb73 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -465,10 +465,9 @@ class JavaPluginsTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/459", "https://github.com/GradleUp/shadow/issues/852", ) - @ParameterizedTest - @ValueSource(booleans = [false, true]) - fun excludeGradleApiByDefault(legacy: Boolean) { - writeGradlePluginModule(legacy) + @Test + fun excludeGradleApiByDefault() { + writeGradlePluginModule() projectScript.appendText( """ dependencies { @@ -503,12 +502,8 @@ class JavaPluginsTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/1422", ) @Test - fun movesLocalGradleApiToCompileOnly() { - projectScript.writeText( - """ - ${getDefaultProjectBuildScript("java-gradle-plugin")} - """.trimIndent() + lineSeparator, - ) + fun moveLocalGradleApiToCompileOnly() { + projectScript.writeText(getDefaultProjectBuildScript("java-gradle-plugin")) val outputCompileOnly = dependencies(COMPILE_ONLY_CONFIGURATION_NAME) val outputApi = dependencies(API_CONFIGURATION_NAME) @@ -524,11 +519,7 @@ class JavaPluginsTest : BasePluginTest() { @ParameterizedTest @ValueSource(strings = [COMPILE_ONLY_CONFIGURATION_NAME, API_CONFIGURATION_NAME]) fun doNotReAddSuppressedGradleApi(configuration: String) { - projectScript.writeText( - """ - ${getDefaultProjectBuildScript("java-gradle-plugin")} - """.trimIndent() + lineSeparator, - ) + projectScript.writeText(getDefaultProjectBuildScript("java-gradle-plugin")) val output = dependencies( configuration = configuration, @@ -588,7 +579,7 @@ class JavaPluginsTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/443", ) @Test - fun registerCustomShadowJarTaskThatContainsDependenciesOnly() { + fun registerCustomShadowJarThatContainsDependenciesOnly() { val mainClassEntry = writeClass() val dependencyShadowJar = "dependencyShadowJar" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index aae5f7f50..2b5afd271 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -46,8 +46,6 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledOnOs import org.junit.jupiter.api.condition.OS import org.junit.jupiter.api.io.TempDir -import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.ValueSource class PublishingTest : BasePluginTest() { @TempDir @@ -62,10 +60,10 @@ class PublishingTest : BasePluginTest() { @DisabledOnOs( OS.WINDOWS, architectures = ["aarch64"], - disabledReason = "https://github.com/gradle/gradle/issues/29807", + disabledReason = "Cannot use toolchain on Windows ARM64", // TODO: https://github.com/gradle/gradle/issues/29807 ) @Test - fun publishShadowJar() { + fun publishShadowJarWithCorrectTargetJvm() { projectScript.appendText( publishConfiguration( shadowBlock = """ @@ -75,20 +73,14 @@ class PublishingTest : BasePluginTest() { ) + lineSeparator, ) - publish() - - val assertions = { variantAttrs: Array>? -> - assertShadowJarCommon(repoJarPath("my/maven-all/1.0/maven-all-1.0.jar")) + val assertions = { variantAttrs: Array> -> + publish() assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) val gmm = gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")) - if (variantAttrs == null) { - assertShadowVariantCommon(gmm) - } else { - assertShadowVariantCommon(gmm, variantAttrs = variantAttrs) - } + assertShadowVariantCommon(gmm, variantAttrs = variantAttrs) } - assertions(null) + assertions(shadowVariantAttrs) val attrsWithoutTargetJvm = shadowVariantAttrs.filterNot { (name, _) -> name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name @@ -104,7 +96,6 @@ class PublishingTest : BasePluginTest() { } """.trimIndent() + lineSeparator, ) - publish() assertions(attrsWithoutTargetJvm + targetJvmAttr17) projectScript.appendText( @@ -114,7 +105,6 @@ class PublishingTest : BasePluginTest() { } """.trimIndent() + lineSeparator, ) - publish() assertions(attrsWithoutTargetJvm + targetJvmAttr11) projectScript.appendText( @@ -124,7 +114,6 @@ class PublishingTest : BasePluginTest() { } """.trimIndent() + lineSeparator, ) - publish() // sourceCompatibility doesn't affect the target JVM version. assertions(attrsWithoutTargetJvm + targetJvmAttr11) @@ -135,7 +124,6 @@ class PublishingTest : BasePluginTest() { } """.trimIndent() + lineSeparator, ) - publish() // options.release flag is honored. assertions(attrsWithoutTargetJvm + targetJvmAttr8) } @@ -195,10 +183,9 @@ class PublishingTest : BasePluginTest() { } } - @ParameterizedTest - @ValueSource(booleans = [false, true]) - fun publishShadowedGradlePlugin(legacy: Boolean) { - writeGradlePluginModule(legacy) + @Test + fun publishShadowedGradlePlugin() { + writeGradlePluginModule() projectScript.appendText( publishConfiguration( projectBlock = """ @@ -387,13 +374,9 @@ class PublishingTest : BasePluginTest() { } @Test - fun publishShadowJarWithGradleMetadata() { + fun publishJarAndShadowJarWithGradleMetadata() { projectScript.appendText( publishConfiguration( - projectBlock = """ - group = 'com.acme' - version = '1.0' - """.trimIndent(), dependenciesBlock = """ implementation 'my:a:1.0' implementation 'my:b:1.0' @@ -413,18 +396,18 @@ class PublishingTest : BasePluginTest() { publish() - assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0.jar")).useAll { + assertThat(repoJarPath("my/maven/1.0/maven-1.0.jar")).useAll { containsNone(*entriesInAB) } - assertThat(repoJarPath("com/acme/maven/1.0/maven-1.0-all.jar")).useAll { + assertThat(repoJarPath("my/maven/1.0/maven-1.0-all.jar")).useAll { containsOnly( *entriesInAB, *manifestEntries, ) } - assertPomCommon(repoPath("com/acme/maven/1.0/maven-1.0.pom"), arrayOf("my:a:1.0", "my:b:1.0")) - gmmAdapter.fromJson(repoPath("com/acme/maven/1.0/maven-1.0.module")).let { gmm -> + assertPomCommon(repoPath("my/maven/1.0/maven-1.0.pom"), arrayOf("my:a:1.0", "my:b:1.0")) + gmmAdapter.fromJson(repoPath("my/maven/1.0/maven-1.0.module")).let { gmm -> // apiElements, runtimeElements, shadowRuntimeElements assertThat(gmm.variantNames).containsOnly( API_ELEMENTS_CONFIGURATION_NAME, @@ -453,8 +436,8 @@ class PublishingTest : BasePluginTest() { assertShadowVariantCommon(gmm) } - assertPomCommon(repoPath("com/acme/maven-all/1.0/maven-all-1.0.pom")) - gmmAdapter.fromJson(repoPath("com/acme/maven-all/1.0/maven-all-1.0.module")).let { gmm -> + assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) + gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")).let { gmm -> assertThat(gmm.variantNames).containsOnly( SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 9fc730aee..31b88b19f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -3,13 +3,11 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains -import assertk.assertions.isEmpty import assertk.assertions.isInstanceOf import assertk.assertions.isNotEmpty import assertk.fail import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.Companion.CONSTANT_TIME_FOR_ZIP_ENTRIES -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.runProcess @@ -26,7 +24,7 @@ import org.opentest4j.AssertionFailedError class RelocationTest : BasePluginTest() { @ParameterizedTest - @MethodSource("prefixProvider") + @ValueSource(strings = ["foo", "new.pkg", "new/path"]) fun autoRelocation(relocationPrefix: String) { val mainClassEntry = writeClass() projectScript.appendText( @@ -69,6 +67,47 @@ class RelocationTest : BasePluginTest() { ) } + @ParameterizedTest + @MethodSource("relocationCliOptionProvider") + fun enableAutoRelocationByCliOption(enable: Boolean, relocationPrefix: String) { + val mainClassEntry = writeClass() + projectScript.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + """.trimIndent(), + ) + val relocatedEntries = junitEntries.map { "$relocationPrefix/$it" } + .filterNot { it.startsWith("$relocationPrefix/META-INF/") } + .toTypedArray() + + if (enable) { + run(shadowJarPath, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") + } else { + run(shadowJarPath, "--relocation-prefix=$relocationPrefix") + } + + assertThat(outputShadowedJar).useAll { + if (enable) { + containsOnly( + "my/", + "$relocationPrefix/", + mainClassEntry, + *relocatedEntries, + *manifestEntries, + ) + } else { + containsOnly( + "my/", + mainClassEntry, + *junitEntries, + *manifestEntries, + ) + } + } + } + @Issue( "https://github.com/GradleUp/shadow/issues/58", ) @@ -208,76 +247,6 @@ class RelocationTest : BasePluginTest() { } } - @Test - fun relocateDoesNotDropDependencyResources() { - settingsScript.appendText( - """ - include 'core', 'app' - """.trimIndent(), - ) - path("core/build.gradle").writeText( - """ - plugins { - id 'java-library' - } - dependencies { - api 'junit:junit:3.8.2' - } - """.trimIndent(), - ) - - path("core/src/main/resources/TEST").writeText("TEST RESOURCE") - path("core/src/main/resources/test.properties").writeText("name=test") - path("core/src/main/java/core/Core.java").writeText( - """ - package core; - import junit.framework.Test; - public class Core {} - """.trimIndent(), - ) - - path("app/build.gradle").writeText( - """ - ${getDefaultProjectBuildScript()} - dependencies { - implementation project(':core') - } - $shadowJarTask { - relocate 'core', 'app.core' - relocate 'junit.framework', 'app.junit.framework' - } - """.trimIndent(), - ) - path("app/src/main/resources/APP-TEST").writeText("APP TEST RESOURCE") - path("app/src/main/java/app/App.java").writeText( - """ - package app; - import core.Core; - import junit.framework.Test; - public class App {} - """.trimIndent(), - ) - val relocatedEntries = junitEntries - .map { it.replace("junit/framework/", "app/junit/framework/") }.toTypedArray() - - run(":app:$SHADOW_JAR_TASK_NAME") - - assertThat(jarPath("app/build/libs/app-all.jar")).useAll { - containsOnly( - "TEST", - "APP-TEST", - "test.properties", - "app/", - "app/core/", - "app/junit/", - "app/App.class", - "app/core/Core.class", - *relocatedEntries, - *manifestEntries, - ) - } - } - @Issue( "https://github.com/GradleUp/shadow/issues/93", "https://github.com/GradleUp/shadow/issues/114", @@ -403,7 +372,7 @@ class RelocationTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/884", ) @Test - fun preserveKotlinBuiltins() { + fun excludeKotlinBuiltinsFromRelocation() { val kotlinJar = buildJar("kotlin.jar") { insert("kotlin/kotlin.kotlin_builtins", "This is a Kotlin builtins file.") } @@ -501,47 +470,6 @@ class RelocationTest : BasePluginTest() { } } - @ParameterizedTest - @MethodSource("relocationCliOptionProvider") - fun enableAutoRelocationByCliOption(enable: Boolean, relocationPrefix: String) { - val mainClassEntry = writeClass() - projectScript.appendText( - """ - dependencies { - implementation 'junit:junit:3.8.2' - } - """.trimIndent(), - ) - val relocatedEntries = junitEntries.map { "$relocationPrefix/$it" } - .filterNot { it.startsWith("$relocationPrefix/META-INF/") } - .toTypedArray() - - if (enable) { - run(shadowJarPath, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") - } else { - run(shadowJarPath, "--relocation-prefix=$relocationPrefix") - } - - assertThat(outputShadowedJar).useAll { - if (enable) { - containsOnly( - "my/", - "$relocationPrefix/", - mainClassEntry, - *relocatedEntries, - *manifestEntries, - ) - } else { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) - } - } - } - @Test fun relocateStringConstantsByDefault() { writeClassWithStringRef() @@ -571,7 +499,7 @@ class RelocationTest : BasePluginTest() { ) @ParameterizedTest @ValueSource(booleans = [false, true]) - fun disableRelocateStringConstants(skipStringConstants: Boolean) { + fun disableStringConstantsRelocation(skipStringConstants: Boolean) { writeClassWithStringRef() projectScript.appendText( """ @@ -621,13 +549,6 @@ class RelocationTest : BasePluginTest() { } private companion object { - @JvmStatic - fun prefixProvider() = listOf( - Arguments.of("foo"), - Arguments.of("new.pkg"), - Arguments.of("new/path"), - ) - @JvmStatic fun preserveLastModifiedProvider() = listOf( Arguments.of(false, false), diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 67f456800..edc224d9e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -82,7 +82,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } @Test - fun serviceResourceTransformerRelocation() { + fun serviceResourceTransformerWithRelocation() { val one = buildJarOne { insert( "META-INF/services/java.sql.Driver", @@ -164,7 +164,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { "https://github.com/GradleUp/shadow/issues/71", ) @Test - fun applyTransformersToProjectResources() { + fun transformProjectResources() { val servicesBarEntry = "META-INF/services/foo.Bar" val one = buildJarOne { insert(servicesBarEntry, CONTENT_ONE) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 18bc0ed95..1f029f94b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -5,7 +5,6 @@ import assertk.assertThat import assertk.assertions.isEqualTo import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull -import assertk.assertions.isNull import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.util.Issue @@ -57,25 +56,6 @@ class TransformersTest : BaseTransformerTest() { commonAssertions() } - @Issue( - "https://github.com/GradleUp/shadow/issues/82", - ) - @Test - fun shadowManifestLeaksToJarManifest() { - writeClass() - projectScript.appendText(MANIFEST_ATTRS) - - run("jar", shadowJarPath) - - commonAssertions() - - val mf = jarPath("build/libs/my-1.0.jar").use { it.manifest } - assertThat(mf).isNotNull() - assertThat(mf.mainAttributes.getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("FAILED") - assertThat(mf.mainAttributes.getValue(mainClassAttributeKey)).isEqualTo("my.Main") - assertThat(mf.mainAttributes.getValue(NEW_ENTRY_ATTR_KEY)).isNull() - } - @Issue( "https://github.com/GradleUp/shadow/issues/427", ) From 196754dcedb4bb2992ed092ee5565da9241e3b62 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 17 Aug 2025 14:41:17 +0800 Subject: [PATCH 689/941] Polish PublishingTest (#1664) * Check artifact entries * Remove outdated `publishMultiProjectShadowJar` * Revert "Test publish jars that depend on shadow jars" --- .../gradle/plugins/shadow/PublishingTest.kt | 203 +++++++----------- 1 file changed, 81 insertions(+), 122 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 2b5afd271..64279fea4 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -28,7 +28,6 @@ import kotlin.io.path.inputStream import kotlin.io.path.listDirectoryEntries import kotlin.io.path.name import kotlin.io.path.readText -import kotlin.io.path.writeText import org.apache.maven.model.Dependency import org.apache.maven.model.Model import org.apache.maven.model.io.xpp3.MavenXpp3Reader @@ -145,9 +144,27 @@ class PublishingTest : BasePluginTest() { publish() - assertShadowJarCommon(repoJarPath("my/maven/1.0/maven-1.0.jar")) - assertPomCommon(repoPath("my/maven/1.0/maven-1.0.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my/maven/1.0/maven-1.0.module"))) + val artifactRoot = "my/maven/1.0" + assertThat(repoPath(artifactRoot).entries).containsOnly( + "maven-1.0.jar", + "maven-1.0.module", + "maven-1.0.pom", + "maven-1.0.jar.md5", + "maven-1.0.module.md5", + "maven-1.0.pom.md5", + "maven-1.0.jar.sha1", + "maven-1.0.module.sha1", + "maven-1.0.pom.sha1", + "maven-1.0.jar.sha256", + "maven-1.0.module.sha256", + "maven-1.0.pom.sha256", + "maven-1.0.jar.sha512", + "maven-1.0.module.sha512", + "maven-1.0.pom.sha512", + ) + assertShadowJarCommon(repoJarPath("$artifactRoot/maven-1.0.jar")) + assertPomCommon(repoPath("$artifactRoot/maven-1.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("$artifactRoot/maven-1.0.module"))) } @Test @@ -207,16 +224,15 @@ class PublishingTest : BasePluginTest() { publish() val artifactRoot = "my/plugin/my-gradle-plugin/1.0" - assertThat(repoPath(artifactRoot).listDirectoryEntries("*.jar").map(Path::name)).containsOnly( + assertThat(repoPath(artifactRoot).entries.filter { it.endsWith(".jar") }).containsOnly( "my-gradle-plugin-1.0.jar", "my-gradle-plugin-1.0-javadoc.jar", "my-gradle-plugin-1.0-sources.jar", ) - val artifactPrefix = "$artifactRoot/my-gradle-plugin-1.0" - assertShadowJarCommon(repoJarPath("$artifactPrefix.jar")) - assertPomCommon(repoPath("$artifactPrefix.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("$artifactPrefix.module"))) + assertShadowJarCommon(repoJarPath("$artifactRoot/my-gradle-plugin-1.0.jar")) + assertPomCommon(repoPath("$artifactRoot/my-gradle-plugin-1.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("$artifactRoot/my-gradle-plugin-1.0.module"))) } @Issue( @@ -248,7 +264,8 @@ class PublishingTest : BasePluginTest() { publish() - assertThat(repoPath("my-group/my-artifact/2.0/").listDirectoryEntries().map { it.name }).containsOnly( + val artifactRoot = "my-group/my-artifact/2.0" + assertThat(repoPath(artifactRoot).entries).containsOnly( "my-artifact-2.0-my-classifier.my-ext.sha512", "my-artifact-2.0-my-classifier.my-ext", "my-artifact-2.0.pom.sha256", @@ -266,111 +283,9 @@ class PublishingTest : BasePluginTest() { "my-artifact-2.0.pom.sha1", ) - assertShadowJarCommon(repoJarPath("my-group/my-artifact/2.0/my-artifact-2.0-my-classifier.my-ext")) - assertPomCommon(repoPath("my-group/my-artifact/2.0/my-artifact-2.0.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my-group/my-artifact/2.0/my-artifact-2.0.module"))) - } - - @Test - fun publishMultiProjectShadowJar() { - settingsScript.appendText( - """ - include 'a', 'b', 'c' - """.trimIndent(), - ) - projectScript.writeText( - """ - subprojects { - apply plugin: 'java' - apply plugin: 'maven-publish' - version = '1.0' - group = 'my' - } - """.trimIndent(), - ) - - path("a/src/main/resources/aa.properties").writeText("aa") - path("a/src/main/resources/aa2.properties").writeText("aa2") - path("b/src/main/resources/bb.properties").writeText("bb") - - val publishBlock = publishConfiguration( - dependenciesBlock = """ - implementation project(':a') - shadow project(':b') - """.trimIndent(), - shadowBlock = """ - archiveClassifier = '' - archiveBaseName = 'maven-all' - """.trimIndent(), - ) - path("c/build.gradle").writeText( - """ - ${getDefaultProjectBuildScript(withGroup = true, withVersion = true)} - $publishBlock - """.trimIndent(), - ) - - publish() - - assertThat(repoJarPath("my/maven-all/1.0/maven-all-1.0.jar")).useAll { - containsOnly( - "aa.properties", - "aa2.properties", - *manifestEntries, - ) - } - assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module"))) - } - - @Test - fun publishJarThatDependsOnShadowJar() { - writeClientAndServerModules(clientShadowed = true) - path("client/build.gradle").appendText( - publishingBlock( - projectBlock = "group = 'example'", - publicationsBlock = """ - shadow(MavenPublication) { - from components.shadow - } - """.trimIndent(), - ), - ) - path("server/build.gradle").appendText( - publishingBlock( - projectBlock = "group = 'example'", - publicationsBlock = """ - java(MavenPublication) { - from components.java - } - """.trimIndent(), - ), - ) - - publish() - - gmmAdapter.fromJson(repoPath("example/server/1.0/server-1.0.module")).let { gmm -> - assertThat(gmm.variantNames).containsOnly( - API_ELEMENTS_CONFIGURATION_NAME, - RUNTIME_ELEMENTS_CONFIGURATION_NAME, - SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, - ) - assertThat(gmm.runtimeElementsVariant.coordinates).containsOnly( - "example:client:1.0", - ) - assertThat(gmm.shadowRuntimeElementsVariant.coordinates).isEmpty() - assertShadowVariantCommon(gmm, coordinates = emptyArray()) { - transform { it.fileNames }.single().isEqualTo("server-1.0-all.jar") - } - } - gmmAdapter.fromJson(repoPath("example/client/1.0/client-1.0.module")).let { gmm -> - assertThat(gmm.variantNames).containsOnly( - SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, - ) - assertShadowVariantCommon(gmm, coordinates = emptyArray()) { - transform { it.fileNames }.single().isEqualTo("client-1.0-all.jar") - } - } + assertShadowJarCommon(repoJarPath("$artifactRoot/my-artifact-2.0-my-classifier.my-ext")) + assertPomCommon(repoPath("$artifactRoot/my-artifact-2.0.pom")) + assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("$artifactRoot/my-artifact-2.0.module"))) } @Test @@ -396,6 +311,48 @@ class PublishingTest : BasePluginTest() { publish() + assertThat(repoPath("my/maven/1.0").entries).containsOnly( + // Entries of maven-1.0.jar + "maven-1.0.jar", + "maven-1.0.module", + "maven-1.0.pom", + "maven-1.0.jar.md5", + "maven-1.0.module.md5", + "maven-1.0.pom.md5", + "maven-1.0.jar.sha1", + "maven-1.0.module.sha1", + "maven-1.0.pom.sha1", + "maven-1.0.jar.sha256", + "maven-1.0.module.sha256", + "maven-1.0.pom.sha256", + "maven-1.0.jar.sha512", + "maven-1.0.module.sha512", + "maven-1.0.pom.sha512", + // Entries of maven-1.0-all.jar + "maven-1.0-all.jar", + "maven-1.0-all.jar.md5", + "maven-1.0-all.jar.sha1", + "maven-1.0-all.jar.sha256", + "maven-1.0-all.jar.sha512", + ) + assertThat(repoPath("my/maven-all/1.0").entries).containsOnly( + "maven-all-1.0-all.jar", + "maven-all-1.0.module", + "maven-all-1.0.pom", + "maven-all-1.0-all.jar.md5", + "maven-all-1.0.module.md5", + "maven-all-1.0.pom.md5", + "maven-all-1.0-all.jar.sha1", + "maven-all-1.0.module.sha1", + "maven-all-1.0.pom.sha1", + "maven-all-1.0-all.jar.sha256", + "maven-all-1.0.module.sha256", + "maven-all-1.0.pom.sha256", + "maven-all-1.0-all.jar.sha512", + "maven-all-1.0.module.sha512", + "maven-all-1.0.pom.sha512", + ) + assertThat(repoJarPath("my/maven/1.0/maven-1.0.jar")).useAll { containsNone(*entriesInAB) } @@ -472,13 +429,13 @@ class PublishingTest : BasePluginTest() { """.trimIndent(), ): String { return """ - dependencies { - $dependenciesBlock - } - $shadowJarTask { - $shadowBlock - } - ${publishingBlock(projectBlock = projectBlock, publicationsBlock = publicationsBlock)} + dependencies { + $dependenciesBlock + } + $shadowJarTask { + $shadowBlock + } + ${publishingBlock(projectBlock = projectBlock, publicationsBlock = publicationsBlock)} """.trimIndent() } @@ -551,5 +508,7 @@ class PublishingTest : BasePluginTest() { fun MavenXpp3Reader.read(path: Path): Model = path.inputStream().use { read(it) } fun JsonAdapter.fromJson(path: Path): T = requireNotNull(fromJson(path.readText())) + + val Path.entries: List get() = listDirectoryEntries().map { it.name } } } From 70df025ed07bef3aae68806ae683a16d72df3faf Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 18 Aug 2025 22:15:10 +0800 Subject: [PATCH 690/941] Rename attr to attrs --- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 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 23310954f..f02c10db3 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 @@ -55,14 +55,14 @@ public abstract class ShadowJavaPlugin @Inject constructor( it.extendsFrom(shadowConfiguration) it.isCanBeConsumed = true it.isCanBeResolved = false - it.attributes { attr -> - attr.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) - attr.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, Category.LIBRARY)) - attr.attribute( + it.attributes { attrs -> + attrs.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) + attrs.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, Category.LIBRARY)) + attrs.attribute( LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements::class.java, LibraryElements.JAR), ) - attr.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling::class.java, Bundling.SHADOWED)) + attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling::class.java, Bundling.SHADOWED)) val targetJvmVersion = configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME) .map { compileClasspath -> compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) @@ -70,7 +70,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( } // Track JavaPluginExtension to update targetJvmVersion when it changes. - attr.attributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) + attrs.attributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) } it.outgoing.artifact(tasks.shadowJar) } From d91b8d6bb394b0f7297346920e6d2330b3740ec3 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 18 Aug 2025 22:54:49 +0800 Subject: [PATCH 691/941] Fix changelog items --- docs/changes/README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index bb16585e1..74de2118d 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -8,10 +8,10 @@ ### Fixed -- Fix missing space in `ApacheNoticeResourceTransformer` preamble causing malformed NOTICE header. ([#1623](https://github.com/GradleUp/shadow/pull/1623)). -- Fix using `ApacheNoticeResourceTransformer` without `projectName`. ([#1627](https://github.com/GradleUp/shadow/pull/1627)). -- Fix extra indents of `ApacheNoticeResourceTransformer` output. ([#1628](https://github.com/GradleUp/shadow/pull/1628)). -- Fix resolving BOM dependencies when `minimize` is enabled. ([#1637](https://github.com/GradleUp/shadow/pull/1637)). +- Fix missing space in `ApacheNoticeResourceTransformer` preamble causing malformed NOTICE header. ([#1623](https://github.com/GradleUp/shadow/pull/1623)) +- Fix using `ApacheNoticeResourceTransformer` without `projectName`. ([#1627](https://github.com/GradleUp/shadow/pull/1627)) +- Fix extra indents of `ApacheNoticeResourceTransformer` output. ([#1628](https://github.com/GradleUp/shadow/pull/1628)) +- Fix resolving BOM dependencies when `minimize` is enabled. ([#1637](https://github.com/GradleUp/shadow/pull/1637)) ## [9.0.1](https://github.com/GradleUp/shadow/releases/tag/9.0.1) - 2025-08-09 @@ -693,7 +693,7 @@ If you used Shadow for merging service files, the following steps are recommende ### Fixed -- Revert "Bump Java level to 11" ([#1011](https://github.com/GradleUp/shadow/issues/1011)). +- Revert "Bump Java level to 11". ([#1011](https://github.com/GradleUp/shadow/issues/1011)) This reverts the change to maintain compatibility with 8.x versions. The Java level will be bumped to 11 or above in the next major release. From 27424b1cd600dc34dea4574d5a04c27022670215 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 08:29:42 +0800 Subject: [PATCH 692/941] Update plugin android-lint to v8.12.1 (#1667) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 686bd6246..7eef51b07 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -32,7 +32,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.12.0" +android-lint = "com.android.lint:8.12.1" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From eee16e0abe8735cd47a17e9edb30df04aa263749 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 19 Aug 2025 12:29:59 +0800 Subject: [PATCH 693/941] Don't inject TargetJvmVersion attribute when automatic JVM targeting is disabled (#1666) * Return null for Int.MAX_VALUE * Use provider * Update changelog * Test `dontInjectTargetJvmVersionWhenAutoTargetJvmDisabled` * Don't pass null * Revert "Don't pass null" This reverts commit 541b5e4d140dcbc8cf85c244d45d664ed67edbd1. * Check and throw * Use `afterEvaluate` and fix the test * Cleanups --- docs/changes/README.md | 4 +++ .../gradle/plugins/shadow/PublishingTest.kt | 35 ++++++++++++++++++- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 26 +++++++++----- 3 files changed, 55 insertions(+), 10 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 74de2118d..b1970711f 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.2...HEAD) - 2025-xx-xx +## Changed + +- Don't inject `TargetJvmVersion` attribute when automatic JVM targeting is disabled. ([#1666](https://github.com/GradleUp/shadow/pull/1666)) + ## [9.0.2](https://github.com/GradleUp/shadow/releases/tag/9.0.2) - 2025-08-15 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 64279fea4..3b650516e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.Assert import assertk.all import assertk.assertThat +import assertk.assertions.contains import assertk.assertions.containsOnly import assertk.assertions.isEmpty import assertk.assertions.isEqualTo @@ -127,6 +128,38 @@ class PublishingTest : BasePluginTest() { assertions(attrsWithoutTargetJvm + targetJvmAttr8) } + @Issue( + "https://github.com/GradleUp/shadow/issues/1665", + ) + @Test + fun dontInjectTargetJvmVersionWhenAutoTargetJvmDisabled() { + projectScript.appendText( + publishConfiguration( + projectBlock = """ + java { + disableAutoTargetJvm() + } + """.trimIndent(), + shadowBlock = """ + archiveClassifier = '' + archiveBaseName = 'maven-all' + """.trimIndent(), + ), + ) + + val result = publish(infoArgument) + + assertThat(result.output).contains( + "Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases.", + ) + assertShadowVariantCommon( + gmm = gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")), + variantAttrs = shadowVariantAttrs.filterNot { (name, _) -> + name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name + }.toTypedArray(), + ) + } + @Test fun publishShadowJarInsteadOfJar() { projectScript.appendText( @@ -412,7 +445,7 @@ class PublishingTest : BasePluginTest() { return JarPath(remoteRepoPath.resolve(relative)) } - private fun publish(): BuildResult = run("publish") + private fun publish(vararg arguments: String): BuildResult = run("publish", *arguments) private fun publishConfiguration( projectBlock: String = "", 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 f02c10db3..a3982d2c5 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 @@ -51,7 +51,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( compileClasspath.extendsFrom(shadowConfiguration) } @Suppress("EagerGradleConfiguration") // this should be created eagerly. - configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { + val shadowRuntimeElements = configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { it.extendsFrom(shadowConfiguration) it.isCanBeConsumed = true it.isCanBeResolved = false @@ -63,17 +63,25 @@ public abstract class ShadowJavaPlugin @Inject constructor( objects.named(LibraryElements::class.java, LibraryElements.JAR), ) attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling::class.java, Bundling.SHADOWED)) - val targetJvmVersion = configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME) - .map { compileClasspath -> - compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) - ?: javaPluginExtension.targetCompatibility.majorVersion.toInt() - } - - // Track JavaPluginExtension to update targetJvmVersion when it changes. - attrs.attributeProvider(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) } it.outgoing.artifact(tasks.shadowJar) } + + // Must use afterEvaluate here as we need to check the value of targetJvmVersion and track its changes. + afterEvaluate { + val targetJvmVersion = configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME).map { compileClasspath -> + compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) + }.getOrElse(javaPluginExtension.targetCompatibility.majorVersion.toInt()) + + // https://github.com/gradle/gradle/blob/4198ab0670df14af9f77b9098dc892b199ac1f3f/platforms/jvm/plugins-java-base/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmLanguageUtilities.java#L85-L87 + if (targetJvmVersion == Int.MAX_VALUE) { + logger.info("Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases.") + } else { + shadowRuntimeElements.attributes { attrs -> + attrs.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) + } + } + } } protected open fun Project.configureComponents() { From 42acdb808c8a98670259b25177388a551a909a8d Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 19 Aug 2025 13:34:20 +0800 Subject: [PATCH 694/941] Remove cache-read-only for lint workflow --- .github/workflows/build.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 75462424a..d095c8885 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -49,8 +49,6 @@ jobs: distribution: 'zulu' java-version: 21 - uses: gradle/actions/setup-gradle@v4 - with: - cache-read-only: true - run: ./gradlew lint # Status check that is required in branch protection rules. From 5765f8aceabadf88e110909ab67f473b1011d00c Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 19 Aug 2025 18:09:32 +0800 Subject: [PATCH 695/941] Group Develocity updates --- .github/renovate.json5 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 62b033c77..5a9df2721 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -15,5 +15,12 @@ "org.ow2.asm:asm-commons" ], }, + { + "groupName": "Develocity", + "matchPackageNames": [ + "com.gradle:develocity-gradle-plugin", + "com.gradle.develocity:com.gradle.develocity.gradle.plugin", + ], + }, ] } From a7f66fb200e8de91b7cbc3bf42e60dfb93f85b90 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 19 Aug 2025 10:20:33 +0000 Subject: [PATCH 696/941] Update Develocity to v4.1.1 (#1670) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7eef51b07..e032a86b9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,7 +19,7 @@ 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:1.0.0" -develocity = "com.gradle:develocity-gradle-plugin:4.1" +develocity = "com.gradle:develocity-gradle-plugin:4.1.1" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } diff --git a/settings.gradle.kts b/settings.gradle.kts index ed3a041d3..4cdbd29b4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "4.1" + id("com.gradle.develocity") version "4.1.1" } develocity { From c9e6b2f36623295663cd2d760fc6884c44a22329 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Aug 2025 13:28:51 +0800 Subject: [PATCH 697/941] Group jdk release version (#1673) --- build.gradle.kts | 5 ++--- gradle/libs.versions.toml | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 54cb1781d..04359157a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -41,8 +41,7 @@ kotlin { languageVersion = apiVersion jvmTarget = JvmTarget.JVM_11 jvmDefault = JvmDefaultMode.NO_COMPATIBILITY - // Sync with `JavaCompile.options.release`. - freeCompilerArgs.add("-Xjdk-release=11") + freeCompilerArgs.add("-Xjdk-release=${libs.versions.jdkRelease.get()}") } } @@ -218,7 +217,7 @@ kotlin.target.compilations { } tasks.withType().configureEach { - options.release = 11 + options.release = libs.versions.jdkRelease.get().toInt() } tasks.pluginUnderTestMetadata { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e032a86b9..366abf969 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,4 +1,5 @@ [versions] +jdkRelease = "11" kotlin = "2.2.10" moshi = "1.15.2" pluginPublish = "1.3.1" From 9b4d15866686c9b499a1c5e955a739b571ef2c47 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Aug 2025 19:58:30 +0800 Subject: [PATCH 698/941] Allow opting out of shadowRuntimeElements variant (#1662) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow for opt-out of the java shadowRuntimeElements variant * Add `ShadowExtension.getAddOptionalJavaVariant` * Test `publishShadowVariantJar` * `mapToOptional` must be called in `afterEvaluate` * Cleanups * Fix merge * Rename `addOptionalJavaVariant` to `addShadowVariantIntoJavaComponent` * Document `Shadow Variant in Default Java Component` * Update changelog --------- Co-authored-by: Maximilian Müller --- api/shadow.api | 2 + docs/changes/README.md | 4 + docs/publishing/README.md | 58 ++++++++++ .../gradle/plugins/shadow/JavaPluginsTest.kt | 4 + .../gradle/plugins/shadow/PublishingTest.kt | 104 ++++++++++++++++++ .../gradle/plugins/shadow/ShadowBasePlugin.kt | 10 +- .../gradle/plugins/shadow/ShadowExtension.kt | 15 ++- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 13 ++- 8 files changed, 200 insertions(+), 10 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index e2b278bb2..a8a91dad2 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -30,10 +30,12 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugi } public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$Companion { + public final synthetic fun getShadow (Lorg/gradle/api/Project;)Lcom/github/jengelman/gradle/plugins/shadow/ShadowExtension; public final synthetic fun getShadow (Lorg/gradle/api/artifacts/ConfigurationContainer;)Lorg/gradle/api/NamedDomainObjectProvider; } public abstract interface class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { + public abstract fun getAddShadowVariantIntoJavaComponent ()Lorg/gradle/api/provider/Property; } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin : org/gradle/api/Plugin { diff --git a/docs/changes/README.md b/docs/changes/README.md index b1970711f..a7f50a13b 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -5,6 +5,10 @@ ## Changed +- Allow opting out of `shadowRuntimeElements` variant. ([#1662](https://github.com/GradleUp/shadow/pull/1662)) + +## Changed + - Don't inject `TargetJvmVersion` attribute when automatic JVM targeting is disabled. ([#1666](https://github.com/GradleUp/shadow/pull/1666)) diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 1c07c40d3..a8feeccc7 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -49,6 +49,64 @@ artifact and dependencies in the POM file. } ``` +## Shadow Variant in Default Java Component + +The Shadow plugin adds an optional variant to the `java` component when publishing. This variant contains the shadowed +JAR. This allows consumers of the published library to choose between the standard JAR and the shadowed JAR. + +This feature is enabled by default. It can be disabled by setting the `addShadowVariantIntoJavaComponent` property in +the `shadow` extension to `false`. If you want to publish the standard JAR only, disable this feature like: + +=== "Kotlin" + + ```kotlin + plugins { + java + `maven-publish` + id("com.gradleup.shadow") + } + + shadow { + addShadowVariantIntoJavaComponent = false + } + + publishing { + publications { + create("shadow") { + from(components["shadow"]) + } + } + repositories { + maven("https://repo.myorg.com") + } + } + ``` + +=== "Groovy" + + ```groovy + plugins { + id 'java' + id 'maven-publish' + id 'com.gradleup.shadow' + } + + shadow { + addShadowVariantIntoJavaComponent = false + } + + publishing { + publications { + shadow(MavenPublication) { + from components.shadow + } + } + repositories { + maven { url = 'https://repo.myorg.com' } + } + } + ``` + ## Shadow Configuration and Publishing The Shadow plugin provides a custom configuration (`configurations.shadow`) to specify diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 98a25cb73..dfc1da6c9 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -66,6 +66,10 @@ class JavaPluginsTest : BasePluginTest() { assertThat(project.plugins.hasPlugin(LegacyShadowPlugin::class.java)).isTrue() assertThat(project.tasks.findByName(SHADOW_JAR_TASK_NAME)).isNull() + with(project.extensions.getByType(ShadowExtension::class.java)) { + assertThat(addShadowVariantIntoJavaComponent.get()).isTrue() + } + project.plugins.apply(JavaPlugin::class.java) val shadowTask = project.tasks.getByName(SHADOW_JAR_TASK_NAME) as ShadowJar val shadowConfig = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 3b650516e..24dc887bc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -46,6 +46,8 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledOnOs import org.junit.jupiter.api.condition.OS import org.junit.jupiter.api.io.TempDir +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource class PublishingTest : BasePluginTest() { @TempDir @@ -435,6 +437,108 @@ class PublishingTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/651", + ) + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun publishShadowVariantJar(addShadowVariant: Boolean) { + projectScript.appendText( + publishingBlock( + projectBlock = """ + dependencies { + implementation 'my:a:1.0' + shadow 'my:b:1.0' + } + shadow { + addShadowVariantIntoJavaComponent = $addShadowVariant + } + """.trimIndent(), + publicationsBlock = """ + shadow(MavenPublication) { + from components.java + } + """.trimIndent(), + ), + ) + + publish() + + val assertVariantsCommon = { gmm: GradleModuleMetadata -> + assertThat(gmm.apiElementsVariant).all { + transform { it.attributes }.containsOnly( + *commonVariantAttrs, + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_API, + ) + transform { it.coordinates }.isEmpty() + } + assertThat(gmm.runtimeElementsVariant).all { + transform { it.attributes }.containsOnly( + *commonVariantAttrs, + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + ) + transform { it.coordinates }.containsOnly( + "my:a:1.0", + ) + } + } + val entriesCommon = arrayOf( + "maven-1.0.jar", + "maven-1.0.jar.md5", + "maven-1.0.jar.sha1", + "maven-1.0.jar.sha256", + "maven-1.0.jar.sha512", + "maven-1.0.module", + "maven-1.0.module.md5", + "maven-1.0.module.sha1", + "maven-1.0.module.sha256", + "maven-1.0.module.sha512", + "maven-1.0.pom", + "maven-1.0.pom.md5", + "maven-1.0.pom.sha1", + "maven-1.0.pom.sha256", + "maven-1.0.pom.sha512", + ) + val artifactEntries = repoPath("my/maven/1.0/").entries + val gmm = gmmAdapter.fromJson(repoPath("my/maven/1.0/maven-1.0.module")) + val pomDependencies = pomReader.read(repoPath("my/maven/1.0/maven-1.0.pom")) + .dependencies.map { it.coordinate to it.scope } + + if (addShadowVariant) { + assertThat(artifactEntries).containsOnly( + "maven-1.0-all.jar", + "maven-1.0-all.jar.md5", + "maven-1.0-all.jar.sha1", + "maven-1.0-all.jar.sha256", + "maven-1.0-all.jar.sha512", + *entriesCommon, + ) + assertThat(gmm.variantNames).containsOnly( + API_ELEMENTS_CONFIGURATION_NAME, + RUNTIME_ELEMENTS_CONFIGURATION_NAME, + SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, + ) + assertVariantsCommon(gmm) + assertShadowVariantCommon(gmm) + assertThat(pomDependencies).containsOnly( + "my:a:1.0" to "runtime", + "my:b:1.0" to "compile", + ) + } else { + assertThat(artifactEntries).containsOnly(*entriesCommon) + assertThat(gmm.variantNames).containsOnly( + API_ELEMENTS_CONFIGURATION_NAME, + RUNTIME_ELEMENTS_CONFIGURATION_NAME, + ) + assertVariantsCommon(gmm) + assertThat(pomDependencies).containsOnly( + "my:a:1.0" to "runtime", + ) + } + } + private fun repoPath(relative: String): Path { return remoteRepoPath.resolve(relative).also { check(it.exists()) { "Path not found: $it" } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 7292e981b..b4d7b0052 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -1,5 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.CONFIGURATION_NAME +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.EXTENSION_NAME import org.gradle.api.GradleException import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin @@ -17,7 +19,9 @@ public abstract class ShadowBasePlugin : Plugin { if (GradleVersion.current() < GradleVersion.version("8.11")) { throw GradleException("This version of Shadow supports Gradle 8.11+ only. Please upgrade.") } - extensions.create(EXTENSION_NAME, ShadowExtension::class.java) + with(extensions.create(EXTENSION_NAME, ShadowExtension::class.java)) { + addShadowVariantIntoJavaComponent.convention(true) + } @Suppress("EagerGradleConfiguration") // this should be created eagerly. configurations.create(CONFIGURATION_NAME) } @@ -45,5 +49,9 @@ public abstract class ShadowBasePlugin : Plugin { @get:JvmSynthetic public inline val ConfigurationContainer.shadow: NamedDomainObjectProvider get() = named(CONFIGURATION_NAME) + + @get:JvmSynthetic + public inline val Project.shadow: ShadowExtension + get() = extensions.getByType(ShadowExtension::class.java) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index c20749974..6fe3df63b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -1,6 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow -/** - * TODO: haven't removed this as I want to address issues like https://github.com/GradleUp/shadow/issues/651 in it. - */ -public interface ShadowExtension +import org.gradle.api.provider.Property + +public interface ShadowExtension { + /** + * If `true`, publishes the [ShadowJavaPlugin.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME] as an optional variant of + * the `java` component. This affects how consumers resolve the published artifact. + * + * Defaults to `true`. + */ + public val addShadowVariantIntoJavaComponent: Property +} 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 a3982d2c5..78ed3bf26 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 @@ -86,16 +86,19 @@ public abstract class ShadowJavaPlugin @Inject constructor( protected open fun Project.configureComponents() { val shadowRuntimeElements = configurations.shadowRuntimeElements.get() - components.named("java", AdhocComponentWithVariants::class.java) { - it.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> - variant.mapToOptional() - } - } val shadowComponent = softwareComponentFactory.adhoc(COMPONENT_NAME) components.add(shadowComponent) shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> variant.mapToMavenScope("runtime") } + afterEvaluate { + if (!shadow.addShadowVariantIntoJavaComponent.get()) return@afterEvaluate + components.named("java", AdhocComponentWithVariants::class.java) { + it.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> + variant.mapToOptional() + } + } + } } protected open fun Project.configureJavaGradlePlugin() { From ea1b1d3a6ddbe0d63cd93e60b98ed7b50bbab5ed Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Aug 2025 20:53:25 +0800 Subject: [PATCH 699/941] Add logging when addShadowVariantIntoJavaComponent is disabled (#1676) --- .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 9 ++++++++- .../jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt | 5 ++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 24dc887bc..6113b9e31 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -5,6 +5,7 @@ import assertk.all import assertk.assertThat import assertk.assertions.contains import assertk.assertions.containsOnly +import assertk.assertions.doesNotContain import assertk.assertions.isEmpty import assertk.assertions.isEqualTo import assertk.assertions.single @@ -462,8 +463,14 @@ class PublishingTest : BasePluginTest() { ), ) - publish() + val result = publish(infoArgument) + val info = "Skipping adding shadowRuntimeElements variant to Java component." + if (addShadowVariant) { + assertThat(result.output).doesNotContain(info) + } else { + assertThat(result.output).contains(info) + } val assertVariantsCommon = { gmm: GradleModuleMetadata -> assertThat(gmm.apiElementsVariant).all { transform { it.attributes }.containsOnly( 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 78ed3bf26..296246202 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 @@ -92,7 +92,10 @@ public abstract class ShadowJavaPlugin @Inject constructor( variant.mapToMavenScope("runtime") } afterEvaluate { - if (!shadow.addShadowVariantIntoJavaComponent.get()) return@afterEvaluate + if (!shadow.addShadowVariantIntoJavaComponent.get()) { + logger.info("Skipping adding ${shadowRuntimeElements.name} variant to Java component.") + return@afterEvaluate + } components.named("java", AdhocComponentWithVariants::class.java) { it.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> variant.mapToOptional() From 10c4cb7f740cd283ad60cdbc0f5c5c900d2cf751 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 20 Aug 2025 21:12:08 +0800 Subject: [PATCH 700/941] Allow opting out of TARGET_JVM_VERSION_ATTRIBUTE (#1674) * New `addTargetJvmVersionAttribute` * Test `dontInjectTargetJvmVersionWhenOptingOut` * Document the property * Update docs/publishing/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 1 + docs/changes/README.md | 3 +- docs/publishing/README.md | 20 +++++++++++++ .../gradle/plugins/shadow/JavaPluginsTest.kt | 1 + .../gradle/plugins/shadow/PublishingTest.kt | 29 +++++++++++++++++++ .../gradle/plugins/shadow/ShadowBasePlugin.kt | 1 + .../gradle/plugins/shadow/ShadowExtension.kt | 9 ++++++ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 4 +++ 8 files changed, 67 insertions(+), 1 deletion(-) diff --git a/api/shadow.api b/api/shadow.api index a8a91dad2..1e8955af6 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -36,6 +36,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$C public abstract interface class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { public abstract fun getAddShadowVariantIntoJavaComponent ()Lorg/gradle/api/provider/Property; + public abstract fun getAddTargetJvmVersionAttribute ()Lorg/gradle/api/provider/Property; } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin : org/gradle/api/Plugin { diff --git a/docs/changes/README.md b/docs/changes/README.md index a7f50a13b..ae8fad21f 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,9 +3,10 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.2...HEAD) - 2025-xx-xx -## Changed +## Added - Allow opting out of `shadowRuntimeElements` variant. ([#1662](https://github.com/GradleUp/shadow/pull/1662)) +- Allow opting out of `TARGET_JVM_VERSION_ATTRIBUTE`. ([#1674](https://github.com/GradleUp/shadow/pull/1674)) ## Changed diff --git a/docs/publishing/README.md b/docs/publishing/README.md index a8feeccc7..771b13fc8 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -107,6 +107,26 @@ the `shadow` extension to `false`. If you want to publish the standard JAR only, } ``` +The target JVM version attribute (`org.gradle.jvm.version`) of the shadowed variant is added by default, it is useful +for consumers to select the correct variant based on their target JVM version. But it may cause issues in some cases, +you can disable this by setting the `addTargetJvmVersionAttribute` property in the `shadow` extension to `false`: + +=== "Kotlin" + + ```kotlin + shadow { + addTargetJvmVersionAttribute = false + } + ``` + +=== "Groovy" + + ```groovy + shadow { + addTargetJvmVersionAttribute = false + } + ``` + ## Shadow Configuration and Publishing The Shadow plugin provides a custom configuration (`configurations.shadow`) to specify diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index dfc1da6c9..cf2412d09 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -68,6 +68,7 @@ class JavaPluginsTest : BasePluginTest() { with(project.extensions.getByType(ShadowExtension::class.java)) { assertThat(addShadowVariantIntoJavaComponent.get()).isTrue() + assertThat(addTargetJvmVersionAttribute.get()).isTrue() } project.plugins.apply(JavaPlugin::class.java) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 6113b9e31..8c2bf9358 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -163,6 +163,35 @@ class PublishingTest : BasePluginTest() { ) } + @Test + fun dontInjectTargetJvmVersionWhenOptingOut() { + projectScript.appendText( + publishConfiguration( + projectBlock = """ + shadow { + addTargetJvmVersionAttribute = false + } + """.trimIndent(), + shadowBlock = """ + archiveClassifier = '' + archiveBaseName = 'maven-all' + """.trimIndent(), + ), + ) + + val result = publish(infoArgument) + + assertThat(result.output).contains( + "Skipping setting org.gradle.jvm.version attribute for shadowRuntimeElements configuration.", + ) + assertShadowVariantCommon( + gmm = gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")), + variantAttrs = shadowVariantAttrs.filterNot { (name, _) -> + name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name + }.toTypedArray(), + ) + } + @Test fun publishShadowJarInsteadOfJar() { projectScript.appendText( diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index b4d7b0052..43c1efddd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -21,6 +21,7 @@ public abstract class ShadowBasePlugin : Plugin { } with(extensions.create(EXTENSION_NAME, ShadowExtension::class.java)) { addShadowVariantIntoJavaComponent.convention(true) + addTargetJvmVersionAttribute.convention(true) } @Suppress("EagerGradleConfiguration") // this should be created eagerly. configurations.create(CONFIGURATION_NAME) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index 6fe3df63b..74ef80658 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow +import org.gradle.api.attributes.java.TargetJvmVersion import org.gradle.api.provider.Property public interface ShadowExtension { @@ -10,4 +11,12 @@ public interface ShadowExtension { * Defaults to `true`. */ public val addShadowVariantIntoJavaComponent: Property + + /** + * If `true`, adds a [TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE] attribute to the Gradle Module Metadata of the + * shadowed JAR. This affects how consumers resolve the published artifact based on the target JVM version. + * + * Defaults to `true`. + */ + public val addTargetJvmVersionAttribute: Property } 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 296246202..9bd81d484 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 @@ -69,6 +69,10 @@ public abstract class ShadowJavaPlugin @Inject constructor( // Must use afterEvaluate here as we need to check the value of targetJvmVersion and track its changes. afterEvaluate { + if (!shadow.addTargetJvmVersionAttribute.get()) { + logger.info("Skipping setting ${TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name} attribute for ${shadowRuntimeElements.name} configuration.") + return@afterEvaluate + } val targetJvmVersion = configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME).map { compileClasspath -> compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) }.getOrElse(javaPluginExtension.targetCompatibility.majorVersion.toInt()) From 400c60146243f41440e6fdf8c2f35ad3b55ed4a3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 21 Aug 2025 10:47:00 +0800 Subject: [PATCH 701/941] Allow opting out of Multi-Release attribute (#1675) * New `addMultiReleaseAttribute` * Move the property into `ShadowJar` * Test `doesNotContainMultiReleaseAttrIfDisabled` * Parameterized test `containsMultiReleaseAttrIfAnyDependencyContainsIt` * Test `containsMultiReleaseAttrByCliOption` * Update CLI options * Document `Adding Multi-Release Manifest Attribute` --- api/shadow.api | 1 + docs/changes/README.md | 1 + docs/configuration/README.md | 30 ++++++++++ docs/getting-started/README.md | 18 +++--- .../gradle/plugins/shadow/JavaPluginsTest.kt | 58 +++++++++++++++++-- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 14 +++++ 6 files changed, 108 insertions(+), 14 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 1e8955af6..cf28c3d2c 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -191,6 +191,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar protected fun copy ()V protected fun createCopyAction ()Lorg/gradle/api/internal/file/copy/CopyAction; public fun dependencies (Lorg/gradle/api/Action;)V + public fun getAddMultiReleaseAttribute ()Lorg/gradle/api/provider/Property; public fun getApiJars ()Lorg/gradle/api/file/ConfigurableFileCollection; protected abstract fun getArchiveOperations ()Lorg/gradle/api/file/ArchiveOperations; public fun getConfigurations ()Lorg/gradle/api/provider/SetProperty; diff --git a/docs/changes/README.md b/docs/changes/README.md index ae8fad21f..801e647b1 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,6 +7,7 @@ - Allow opting out of `shadowRuntimeElements` variant. ([#1662](https://github.com/GradleUp/shadow/pull/1662)) - Allow opting out of `TARGET_JVM_VERSION_ATTRIBUTE`. ([#1674](https://github.com/GradleUp/shadow/pull/1674)) +- Allow opting out of `Multi-Release` attribute. ([#1675](https://github.com/GradleUp/shadow/pull/1675)) ## Changed diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 8086d4cd9..7a38b0051 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -153,6 +153,36 @@ methods on the `shadowJar.manifest` object can be used to configure the upstream } ``` +## Adding Multi-Release Manifest Attribute + +The [`ShadowJar`][ShadowJar] task can automatically add the `Multi-Release` attribute to the JAR manifest if any of +the included dependencies contain this attribute. This is controlled by the `addMultiReleaseAttribute` property. + +By default, `addMultiReleaseAttribute` is set to `true`. When enabled, Shadow will scan all dependencies being merged +into the shadow JAR. If any dependency JAR has the `Multi-Release` manifest attribute set to `true`, Shadow will add +`Multi-Release: true` to the manifest of the resulting shadow JAR. + +You can disable this behavior by setting `addMultiReleaseAttribute` to `false`: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + addMultiReleaseAttribute = false + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + addMultiReleaseAttribute = false + } + ``` + +This is useful if you want to control the presence of the `Multi-Release` attribute manually or avoid inheriting it +from dependencies. + ## Adding Extra Files The [`ShadowJar`][ShadowJar] task is a subclass of the [`Jar`][Jar] task, which means that the[`Jar.from`][Jar.from] diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 5635f73bd..9d8101bcd 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -148,14 +148,16 @@ build script. Passing property values on the command line is particularly helpfu Here are the options that can be passed to the `shadowJar`: ``` ---enable-auto-relocation Enables auto relocation of packages in the dependencies. ---no-enable-auto-relocation Disables option --enable-auto-relocation. ---fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate. ---no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries. ---minimize-jar Minimizes the jar by removing unused classes. ---no-minimize-jar Disables option --minimize-jar. ---relocation-prefix Prefix used for auto relocation of packages in the dependencies. ---rerun Causes the task to be re-run even if up-to-date. +--add-multi-release-attribute Adds the multi-release attribute to the manifest if any dependencies contain it. +--no-add-multi-release-attribute Disables option --add-multi-release-attribute. +--enable-auto-relocation Enables auto relocation of packages in the dependencies. +--no-enable-auto-relocation Disables option --enable-auto-relocation. +--fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate. +--no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries. +--minimize-jar Minimizes the jar by removing unused classes. +--no-minimize-jar Disables option --minimize-jar. +--relocation-prefix Prefix used for auto relocation of packages in the dependencies. +--rerun Causes the task to be re-run even if up-to-date. ``` Also, you can view more information about the [`ShadowJar`][ShadowJar] task by running the following command: diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index cf2412d09..df938894d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -106,6 +106,7 @@ class JavaPluginsTest : BasePluginTest() { containsOnly(project.runtimeConfiguration) } assertThat(failOnDuplicateEntries.get()).isFalse() + assertThat(addMultiReleaseAttribute.get()).isTrue() } assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile) @@ -116,6 +117,8 @@ class JavaPluginsTest : BasePluginTest() { val result = run("help", "--task", shadowJarPath) assertThat(result.output).contains( + "--add-multi-release-attribute Adds the multi-release attribute to the manifest if any dependencies contain it.", + "--no-add-multi-release-attribute Disables option --add-multi-release-attribute.", "--enable-auto-relocation Enables auto relocation of packages in the dependencies.", "--no-enable-auto-relocation Disables option --enable-auto-relocation.", "--fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate.", @@ -249,8 +252,9 @@ class JavaPluginsTest : BasePluginTest() { @Issue( "https://github.com/GradleUp/shadow/issues/449", ) - @Test - fun containsMultiReleaseAttrIfAnyDependencyContainsIt() { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun containsMultiReleaseAttrIfAnyDependencyContainsIt(addAttribute: Boolean) { writeClientAndServerModules() path("client/build.gradle").appendText( """ @@ -261,13 +265,55 @@ class JavaPluginsTest : BasePluginTest() { } """.trimIndent() + lineSeparator, ) + path("server/build.gradle").appendText( + """ + $shadowJarTask { + addMultiReleaseAttribute = $addAttribute + } + """.trimIndent(), + ) - run(serverShadowJarPath) + val result = run(serverShadowJarPath, infoArgument) - assertThat(outputServerShadowedJar).useAll { - transform { it.mainAttrSize }.isGreaterThan(1) - getMainAttr(multiReleaseAttributeKey).isEqualTo("true") + val info = "Skipping adding Multi-Release attribute to the manifest as it is disabled." + if (addAttribute) { + assertThat(result.output).doesNotContain(info) + } else { + assertThat(result.output).contains(info) } + + val expected = if (addAttribute) "true" else null + assertThat(outputServerShadowedJar.use { it.getMainAttr(multiReleaseAttributeKey) }) + .isEqualTo(expected) + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun containsMultiReleaseAttrByCliOption(enable: Boolean) { + writeClientAndServerModules() + path("client/build.gradle").appendText( + """ + $jarTask { + manifest { + attributes '$multiReleaseAttributeKey': 'true' + } + } + """.trimIndent() + lineSeparator, + ) + + val flag = if (enable) "--add-multi-release-attribute" else "--no-add-multi-release-attribute" + val result = run(serverShadowJarPath, infoArgument, flag) + + val info = "Skipping adding Multi-Release attribute to the manifest as it is disabled." + if (enable) { + assertThat(result.output).doesNotContain(info) + } else { + assertThat(result.output).contains(info) + } + + val expected = if (enable) "true" else null + assertThat(outputServerShadowedJar.use { it.getMainAttr(multiReleaseAttributeKey) }) + .isEqualTo(expected) } @Issue( diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 61e2a57e5..b754aa879 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -184,6 +184,16 @@ public abstract class ShadowJar : Jar() { @get:Option(option = "fail-on-duplicate-entries", description = "Fails build if the ZIP entries in the shadowed JAR are duplicate.") public open val failOnDuplicateEntries: Property = objectFactory.property(false) + /** + * Adds the [java.util.jar.Attributes.Name.MULTI_RELEASE] attribute to the manifest of the shadow JAR if any + * dependencies contain the attribute. + * + * Defaults to `true`. + */ + @get:Input + @get:Option(option = "add-multi-release-attribute", description = "Adds the multi-release attribute to the manifest if any dependencies contain it.") + public open val addMultiReleaseAttribute: Property = objectFactory.property(true) + @Internal override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest @@ -443,6 +453,10 @@ public abstract class ShadowJar : Jar() { } private fun injectMultiReleaseAttrIfPresent() { + if (!addMultiReleaseAttribute.get()) { + logger.info("Skipping adding $multiReleaseAttributeKey attribute to the manifest as it is disabled.") + return + } val includeMultiReleaseAttr = includedDependencies.files.any { try { JarFile(it).use { jarFile -> From 9082fd4b90ef76271e083f0959107c637a359510 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 21 Aug 2025 11:48:21 +0800 Subject: [PATCH 702/941] Tweak logging and related tests (#1678) * Tweak CLI option tests * Add more logging * Update info assertions * Cleanups --- .../gradle/plugins/shadow/JavaPluginsTest.kt | 59 ++++++++++--------- .../gradle/plugins/shadow/MinimizeTest.kt | 2 +- .../gradle/plugins/shadow/PublishingTest.kt | 14 ++--- .../gradle/plugins/shadow/RelocationTest.kt | 15 ++--- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 9 ++- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 11 +++- 6 files changed, 62 insertions(+), 48 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index df938894d..4d01bfac2 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -77,9 +77,11 @@ class JavaPluginsTest : BasePluginTest() { val assembleTask = project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME) assertThat(assembleTask.dependsOn).contains(shadowTask) - // Check extended properties. + // Check inherited properties. with(shadowTask as Jar) { - assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.EXCLUDE) + assertThat(group).isEqualTo(LifecycleBasePlugin.BUILD_GROUP) + assertThat(description).isEqualTo("Create a combined JAR of project and runtime dependencies") + assertThat(archiveAppendix.orNull).isNull() assertThat(archiveBaseName.get()).isEqualTo(projectName) assertThat(archiveClassifier.get()).isEqualTo("all") @@ -92,21 +94,22 @@ class JavaPluginsTest : BasePluginTest() { } assertThat(destinationDirectory.get().asFile) .isEqualTo(project.layout.buildDirectory.dir("libs").get().asFile) + + assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.EXCLUDE) } // Check self properties. with(shadowTask) { - assertThat(group).isEqualTo(LifecycleBasePlugin.BUILD_GROUP) - assertThat(description).isEqualTo("Create a combined JAR of project and runtime dependencies") - assertThat(minimizeJar.get()).isFalse() + assertThat(addMultiReleaseAttribute.get()).isTrue() assertThat(enableAutoRelocation.get()).isFalse() + assertThat(failOnDuplicateEntries.get()).isFalse() + assertThat(minimizeJar.get()).isFalse() + assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) assertThat(configurations.get()).all { isNotEmpty() containsOnly(project.runtimeConfiguration) } - assertThat(failOnDuplicateEntries.get()).isFalse() - assertThat(addMultiReleaseAttribute.get()).isTrue() } assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile) @@ -275,16 +278,15 @@ class JavaPluginsTest : BasePluginTest() { val result = run(serverShadowJarPath, infoArgument) - val info = "Skipping adding Multi-Release attribute to the manifest as it is disabled." - if (addAttribute) { - assertThat(result.output).doesNotContain(info) - } else { - assertThat(result.output).contains(info) - } - - val expected = if (addAttribute) "true" else null + assertThat(result.output).contains( + if (addAttribute) { + "Adding Multi-Release attribute to the manifest if any dependencies contain it." + } else { + "Skipping adding Multi-Release attribute to the manifest as it is disabled." + }, + ) assertThat(outputServerShadowedJar.use { it.getMainAttr(multiReleaseAttributeKey) }) - .isEqualTo(expected) + .isEqualTo(if (addAttribute) "true" else null) } @ParameterizedTest @@ -301,19 +303,18 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent() + lineSeparator, ) - val flag = if (enable) "--add-multi-release-attribute" else "--no-add-multi-release-attribute" - val result = run(serverShadowJarPath, infoArgument, flag) - - val info = "Skipping adding Multi-Release attribute to the manifest as it is disabled." - if (enable) { - assertThat(result.output).doesNotContain(info) - } else { - assertThat(result.output).contains(info) - } + val arg = if (enable) "--add-multi-release-attribute" else "--no-add-multi-release-attribute" + val result = run(serverShadowJarPath, infoArgument, arg) - val expected = if (enable) "true" else null + assertThat(result.output).contains( + if (enable) { + "Adding Multi-Release attribute to the manifest if any dependencies contain it." + } else { + "Skipping adding Multi-Release attribute to the manifest as it is disabled." + }, + ) assertThat(outputServerShadowedJar.use { it.getMainAttr(multiReleaseAttributeKey) }) - .isEqualTo(expected) + .isEqualTo(if (enable) "true" else null) } @Issue( @@ -880,9 +881,9 @@ class JavaPluginsTest : BasePluginTest() { val result = run( serverShadowJarPath, ipArgument, + infoArgument, "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a file instead. - infoArgument, ) assertThat(result.output).all { @@ -940,7 +941,7 @@ class JavaPluginsTest : BasePluginTest() { val result = if (enable) { runWithFailure(shadowJarPath, "--fail-on-duplicate-entries") } else { - run(shadowJarPath, infoArgument) + run(shadowJarPath, infoArgument, "--no-fail-on-duplicate-entries") } assertThat(result.output).contains( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index 497d165ed..108305efa 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -252,7 +252,7 @@ class MinimizeTest : BasePluginTest() { if (enable) { run(serverShadowJarPath, "--minimize-jar") } else { - run(serverShadowJarPath) + run(serverShadowJarPath, "--no-minimize-jar") } assertThat(outputServerShadowedJar).useAll { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 8c2bf9358..d7eac8c44 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -5,7 +5,6 @@ import assertk.all import assertk.assertThat import assertk.assertions.contains import assertk.assertions.containsOnly -import assertk.assertions.doesNotContain import assertk.assertions.isEmpty import assertk.assertions.isEqualTo import assertk.assertions.single @@ -494,12 +493,13 @@ class PublishingTest : BasePluginTest() { val result = publish(infoArgument) - val info = "Skipping adding shadowRuntimeElements variant to Java component." - if (addShadowVariant) { - assertThat(result.output).doesNotContain(info) - } else { - assertThat(result.output).contains(info) - } + assertThat(result.output).contains( + if (addShadowVariant) { + "Adding shadowRuntimeElements variant to Java component." + } else { + "Skipping adding shadowRuntimeElements variant to Java component." + }, + ) val assertVariantsCommon = { gmm: GradleModuleMetadata -> assertThat(gmm.apiElementsVariant).all { transform { it.attributes }.containsOnly( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 31b88b19f..238694223 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -85,24 +85,25 @@ class RelocationTest : BasePluginTest() { if (enable) { run(shadowJarPath, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") } else { - run(shadowJarPath, "--relocation-prefix=$relocationPrefix") + run(shadowJarPath, "--no-enable-auto-relocation", "--relocation-prefix=$relocationPrefix") } + val commonEntries = arrayOf( + "my/", + mainClassEntry, + *manifestEntries, + ) assertThat(outputShadowedJar).useAll { if (enable) { containsOnly( - "my/", "$relocationPrefix/", - mainClassEntry, *relocatedEntries, - *manifestEntries, + *commonEntries, ) } else { containsOnly( - "my/", - mainClassEntry, *junitEntries, - *manifestEntries, + *commonEntries, ) } } 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 9bd81d484..2b5088c5b 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 @@ -69,7 +69,9 @@ public abstract class ShadowJavaPlugin @Inject constructor( // Must use afterEvaluate here as we need to check the value of targetJvmVersion and track its changes. afterEvaluate { - if (!shadow.addTargetJvmVersionAttribute.get()) { + if (shadow.addTargetJvmVersionAttribute.get()) { + logger.info("Setting ${TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name} attribute for ${shadowRuntimeElements.name} configuration.") + } else { logger.info("Skipping setting ${TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name} attribute for ${shadowRuntimeElements.name} configuration.") return@afterEvaluate } @@ -81,6 +83,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( if (targetJvmVersion == Int.MAX_VALUE) { logger.info("Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases.") } else { + logger.info("Setting target JVM version to $targetJvmVersion for ${shadowRuntimeElements.name} configuration.") shadowRuntimeElements.attributes { attrs -> attrs.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) } @@ -96,7 +99,9 @@ public abstract class ShadowJavaPlugin @Inject constructor( variant.mapToMavenScope("runtime") } afterEvaluate { - if (!shadow.addShadowVariantIntoJavaComponent.get()) { + if (shadow.addShadowVariantIntoJavaComponent.get()) { + logger.info("Adding ${shadowRuntimeElements.name} variant to Java component.") + } else { logger.info("Skipping adding ${shadowRuntimeElements.name} variant to Java component.") return@afterEvaluate } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index b754aa879..be28cf93c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -439,7 +439,12 @@ public abstract class ShadowJar : Jar() { private val packageRelocators: List get() { - if (!enableAutoRelocation.get()) return emptyList() + if (enableAutoRelocation.get()) { + logger.info("Adding auto relocation packages in the dependencies with prefix '${relocationPrefix.get()}'.") + } else { + logger.info("Skipping package relocators as auto relocation is disabled.") + return emptyList() + } val prefix = relocationPrefix.get() return includedDependencies.files.flatMap { file -> JarFile(file).use { jarFile -> @@ -453,7 +458,9 @@ public abstract class ShadowJar : Jar() { } private fun injectMultiReleaseAttrIfPresent() { - if (!addMultiReleaseAttribute.get()) { + if (addMultiReleaseAttribute.get()) { + logger.info("Adding $multiReleaseAttributeKey attribute to the manifest if any dependencies contain it.") + } else { logger.info("Skipping adding $multiReleaseAttributeKey attribute to the manifest as it is disabled.") return } From 6c61018c3dbbf2b56e55211413374f53ffab6cd7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 21 Aug 2025 07:03:11 +0000 Subject: [PATCH 703/941] Update actions/setup-java action to v5 (#1679) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 6 +++--- .github/workflows/release.yml | 2 +- .github/workflows/update-start-scripts.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d095c8885..be1beee01 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -32,7 +32,7 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: 'zulu' java-version: ${{ matrix.java }} @@ -44,7 +44,7 @@ jobs: runs-on: ubuntu-24.04-arm steps: - uses: actions/checkout@v5 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: 'zulu' java-version: 21 @@ -74,7 +74,7 @@ jobs: if: github.event.repository.fork == false && github.ref == 'refs/heads/main' steps: - uses: actions/checkout@v5 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: 'zulu' java-version: 21 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 00c4345d3..8bd452eaf 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -15,7 +15,7 @@ jobs: contents: write steps: - uses: actions/checkout@v5 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: 'zulu' java-version: 21 diff --git a/.github/workflows/update-start-scripts.yml b/.github/workflows/update-start-scripts.yml index 8278ae41b..b91a9ccb2 100644 --- a/.github/workflows/update-start-scripts.yml +++ b/.github/workflows/update-start-scripts.yml @@ -15,7 +15,7 @@ jobs: if: github.event.repository.fork == false steps: - uses: actions/checkout@v5 - - uses: actions/setup-java@v4 + - uses: actions/setup-java@v5 with: distribution: 'zulu' java-version: 21 From 2a4f1622ab26947a7d3d7dede0f1998fdabdc499 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 21 Aug 2025 17:44:39 +0800 Subject: [PATCH 704/941] Replace debug level with info in logging --- .../jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 7ef82afb5..4dc49651b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -176,7 +176,7 @@ public open class ShadowCopyAction( val className = classPath.substringBeforeLast(".").replace('/', '.') return unusedClasses.contains(className).also { if (it) { - logger.debug("Dropping unused class: $className") + logger.info("Dropping unused class: $className") } } } From 2a49d617a5d5286a55124d6c614c02ffa75212a1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 22 Aug 2025 04:19:37 +0800 Subject: [PATCH 705/941] Improve doc test dir naming (#1681) * Create indexed test dirs in provideDynamicTests * With name * Use `createDirectories` * Note `displayName` * Add `--stacktrace` * Fix nonAlphanumeric names --- .../jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt | 8 +++++--- .../gradle/plugins/shadow/snippet/SnippetExecutable.kt | 5 +++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt index d5e06ffc9..bfc913a80 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt @@ -3,7 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.snippet.CodeSnippetExtractor import com.github.jengelman.gradle.plugins.shadow.snippet.DslLang import java.nio.file.Path -import kotlin.io.path.createTempDirectory +import kotlin.io.path.createDirectory import org.junit.jupiter.api.DynamicTest import org.junit.jupiter.api.TestFactory import org.junit.jupiter.api.io.TempDir @@ -27,9 +27,11 @@ class DocCodeSnippetTest { } return langExecutables.flatten().map { - // Create a temporary directory for each test, root will be deleted after all tests are run. - it.tempDir = createTempDirectory(root) + val dirName = it.displayName.replace(nonAlphanumeric, "_") + it.tempDir = root.resolve(dirName).createDirectory() DynamicTest.dynamicTest(it.displayName, it) } } } + +private val nonAlphanumeric = "[^a-zA-Z0-9]".toRegex() diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index 8a8b2fb95..00c9d6c5c 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -16,6 +16,10 @@ sealed class SnippetExecutable : Executable { abstract val assembleDependsOn: String abstract val snippet: String + + /** + * Unique name for the test, formatted as `publishing/README.md:10`. + */ abstract val displayName: String abstract val exceptionTransformer: (Throwable) -> Throwable @@ -96,6 +100,7 @@ sealed class SnippetExecutable : Executable { .forwardOutput() .withArguments( "--warning-mode=$warningMode", + "--stacktrace", "build", ) .build() From 3b9ad4fe9e376069f876d1bc87dc07cae1439369 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 22 Aug 2025 23:26:58 +0800 Subject: [PATCH 706/941] Remove outdated ConfigureShadowRelocation wording in docs (#1688) * Remove `Automatic package relocation with Shadow prior to v8.1.0` * Update `Automatically Relocating Dependencies` --- docs/configuration/relocation/README.md | 2 -- docs/gradle-plugins/README.md | 13 ------------- 2 files changed, 15 deletions(-) diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index 4148f0ead..f99fbc0c5 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -205,8 +205,6 @@ To configure automatic dependency relocation, set `enableAutoRelocation = true` } ``` -In versions before 8.1.0 it was necessary to configure a separate `ConfigureShadowRelocation` task for this. - > Configuring package auto relocation can add significant time to the shadow process as it will process all dependencies > in the configurations declared to be shadowed. By default, this is the `runtime` or `runtimeClasspath` configurations. > Be mindful that some Gradle plugins will automatically add dependencies to your class path. You may need to remove these diff --git a/docs/gradle-plugins/README.md b/docs/gradle-plugins/README.md index e0f4a30fe..d2278a5d0 100644 --- a/docs/gradle-plugins/README.md +++ b/docs/gradle-plugins/README.md @@ -66,19 +66,6 @@ plugin. See the [Gradle Plugin Publish docs](https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#shadow_dependencies) for details. -## Automatic package relocation with Shadow prior to v8.1.0 - -Prior to Shadow v8.1.0, Shadow handled this by introducing a new task type `ConfigureShadowRelocation`. -Tasks of this type are configured to target an instance of a [`ShadowJar`][ShadowJar] task and run immediately before -it. - -The `ConfigureShadowRelocation` task, scans the dependencies from the configurations specified on the associated -`ShadowJar` task and collects the package names contained within them. It then configures relocation for these -packages using the specified `prefix` on the associated [`ShadowJar`][ShadowJar] task. - -While this is useful for developing Gradle plugins, nothing about the `ConfigureShadowRelocation` task is tied to -Gradle projects. It can be used for standard Java or Groovy projects. - [Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html From c4c6e5db49ff36f6c6c937218b196747991281b1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 23 Aug 2025 00:04:17 +0800 Subject: [PATCH 707/941] Merge Gradle plugin publishing docs into main publishing page (#1689) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/gradle-plugins/README.md | 72 ----------------------------------- docs/kotlin-plugins/README.md | 3 +- docs/publishing/README.md | 43 +++++++++++++++++++++ mkdocs.yml | 1 - 4 files changed, 44 insertions(+), 75 deletions(-) delete mode 100644 docs/gradle-plugins/README.md diff --git a/docs/gradle-plugins/README.md b/docs/gradle-plugins/README.md deleted file mode 100644 index d2278a5d0..000000000 --- a/docs/gradle-plugins/README.md +++ /dev/null @@ -1,72 +0,0 @@ -# Using Shadow to Package Gradle Plugins - -In some scenarios, writing a Gradle plugin can be problematic because your plugin may depend on a version that -conflicts with the same dependency provided by the Gradle runtime. If this is the case, then you can utilize Shadow -to relocate your dependencies to a different package name to avoid the collision. - -Configuring the relocation has always been possible, but the build author is required to know all the package names -beforehand. As of Shadow v8.1.0, automatic package relocation can be enabled by setting the `enabledRelocation` -and `relocationPrefix` settings on any [`ShadowJar`][ShadowJar] task. - -A simple Gradle plugin can use this feature by applying the `shadow` plugin and configuring the [`ShadowJar`][ShadowJar] -task for relocation. - -=== "Kotlin" - - ```kotlin - plugins { - id("com.gradle.plugin-publish") version "latest" - id("com.gradleup.shadow") - } - - dependencies { - implementation("org.jdom:jdom2:2.0.6") - implementation("org.ow2.asm:asm:6.0") - implementation("org.ow2.asm:asm-commons:6.0") - implementation("commons-io:commons-io:2.4") - implementation("org.apache.ant:ant:1.9.4") - implementation("org.codehaus.plexus:plexus-utils:2.0.6") - } - - tasks.shadowJar { - enableAutoRelocation = true - archiveClassifier = "" - } - ``` - -=== "Groovy" - - ```groovy - plugins { - id 'com.gradle.plugin-publish' version 'latest' - id 'com.gradleup.shadow' - } - - dependencies { - implementation 'org.jdom:jdom2:2.0.6' - implementation 'org.ow2.asm:asm:6.0' - implementation 'org.ow2.asm:asm-commons:6.0' - implementation 'commons-io:commons-io:2.4' - implementation 'org.apache.ant:ant:1.9.4' - implementation 'org.codehaus.plexus:plexus-utils:2.0.6' - } - - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - enableAutoRelocation = true - archiveClassifier = '' - } - ``` - -## Publishing shadowed Gradle plugins - -The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. -Starting with this version, plugin projects that apply both Shadow and the Gradle Plugin Publish plugin will be -automatically configured to publish the output of the [`ShadowJar`][ShadowJar] tasks as the consumable artifact for the -plugin. See the -[Gradle Plugin Publish docs](https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#shadow_dependencies) -for details. - - - -[Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html -[ShadowJar]: ../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html diff --git a/docs/kotlin-plugins/README.md b/docs/kotlin-plugins/README.md index 035c38d04..cc7785e88 100644 --- a/docs/kotlin-plugins/README.md +++ b/docs/kotlin-plugins/README.md @@ -61,8 +61,7 @@ Shadow works well for Kotlin JVM projects like Java projects. Here is an example ``` You can mix the Kotlin JVM plugin with `java-gradle-plugin`, `application`, and other Java plugins, -easily organize your build logic for -[Packaging Gradle Plugins](../gradle-plugins/README.md), [Publishing Libraries](../publishing/README.md), +easily organize your build logic for [Publishing Libraries](../publishing/README.md), [Running Applications](../application-plugin/README.md), and so on. ## For Kotlin Multiplatform Plugin diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 771b13fc8..13a094054 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -248,6 +248,49 @@ the `archiveClassifier` of the shadowed JAR like the following: } ``` +## Publishing the Shadowed Gradle Plugins + +The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. +Starting with this version, plugin projects that apply both Shadow and the Gradle Plugin Publish plugin will be +automatically configured to publish the output of the [`ShadowJar`][ShadowJar] tasks as the consumable artifact for the +plugin. See the +[Gradle Plugin Publish docs](https://docs.gradle.org/current/userguide/publishing_gradle_plugins.html#shadow_dependencies) +for details. The only thing you need to do from the Shadow side is to empty the `archiveClassifier` like: + +=== "Kotlin" + + ```kotlin + plugins { + id("com.gradle.plugin-publish") version "latest" + id("com.gradleup.shadow") + } + + dependencies { + // Your plugin dependencies. + } + + tasks.shadowJar { + archiveClassifier = "" + } + ``` + +=== "Groovy" + + ```groovy + plugins { + id 'com.gradle.plugin-publish' version 'latest' + id 'com.gradleup.shadow' + } + + dependencies { + // Your plugin dependencies. + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + archiveClassifier = '' + } + ``` + ## Publish Custom ShadowJar Task Outputs It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via the diff --git a/mkdocs.yml b/mkdocs.yml index 6623e0c17..d35a3f1d1 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -74,7 +74,6 @@ nav: - 'Android Plugins': android-plugins/README.md - 'Publishing': publishing/README.md - 'Multi-Project': multi-project/README.md - - 'Gradle Plugins': gradle-plugins/README.md - 'Changes': changes/README.md - 'API': api/index.html - 'About': about/README.md From e82bd7d849e08978bdc5c111170a7c12f893dbc1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 23 Aug 2025 00:09:13 +0800 Subject: [PATCH 708/941] Update actions/upload-pages-artifact action to v4 (#1690) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/deploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8fa3f31aa..d7bf98331 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -25,7 +25,7 @@ jobs: # Don't cache it to track updates. pip install mkdocs-material mkdocs build - - uses: actions/upload-pages-artifact@v3 + - uses: actions/upload-pages-artifact@v4 with: path: site - uses: actions/deploy-pages@v4 From 855552942fd7edacc9273828bbe54201db3abe80 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 23 Aug 2025 14:36:39 +0800 Subject: [PATCH 709/941] Rearrange the publishing doc sections (#1691) * Add example for `Shadow Configuration and Publishing` * Remove `group` and `version` * Remove `archiveClassifier` * Reuse `retrofitVersion` * Reword titles --- docs/publishing/README.md | 81 +++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 24 deletions(-) diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 13a094054..a2be4715c 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -145,10 +145,6 @@ This automatic configuration occurs _only_ when using the above methods for configuring publishing. If this behavior is not desirable, then publishing **must** be manually configured. -## Publish the Shadowed JAR instead of the Original JAR - -You may want to publish the shadowed JAR instead of the original JAR. This can be done by trimming -the `archiveClassifier` of the shadowed JAR like the following: === "Kotlin" @@ -159,11 +155,8 @@ the `archiveClassifier` of the shadowed JAR like the following: id("com.gradleup.shadow") } - group = "shadow" - version = "1.0" - + val retrofitVersion = "2.12.0" dependencies { - val retrofitVersion = "2.12.0" // This will be bundled in the shadowed JAR and not declared in the POM. implementation("com.squareup.retrofit2:retrofit:$retrofitVersion") // This will be excluded from the shadowed JAR but declared as a runtime dependency in `META-INF/MANIFEST.MF` @@ -173,10 +166,6 @@ the `archiveClassifier` of the shadowed JAR like the following: compileOnly("com.squareup.retrofit2:converter-scalars:$retrofitVersion") } - tasks.shadowJar { - archiveClassifier = "" - } - publishing { publications { create("shadow") { @@ -188,7 +177,7 @@ the `archiveClassifier` of the shadowed JAR like the following: val node = (dependenciesNode as groovy.util.Node).appendNode("dependency") node.appendNode("groupId", "com.squareup.retrofit2") node.appendNode("artifactId", "converter-gson") - node.appendNode("version", "2.12.0") + node.appendNode("version", retrofitVersion) node.appendNode("scope", "runtime") } } @@ -208,11 +197,8 @@ the `archiveClassifier` of the shadowed JAR like the following: id 'com.gradleup.shadow' } - group = 'shadow' - version = '1.0' - + def retrofitVersion = '2.12.0' dependencies { - def retrofitVersion = '2.12.0' // This will be bundled in the shadowed JAR and not declared in the POM. implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" // This will be excluded from the shadowed JAR but declared as a runtime dependency in `META-INF/MANIFEST.MF` @@ -222,10 +208,6 @@ the `archiveClassifier` of the shadowed JAR like the following: compileOnly "com.squareup.retrofit2:converter-scalars:$retrofitVersion" } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - archiveClassifier = '' - } - publishing { publications { shadow(MavenPublication) { @@ -237,7 +219,7 @@ the `archiveClassifier` of the shadowed JAR like the following: def node = dependenciesNode.appendNode('dependency') node.appendNode('groupId', 'com.squareup.retrofit2') node.appendNode('artifactId', 'converter-gson') - node.appendNode('version', '2.12.0') + node.appendNode('version', retrofitVersion) node.appendNode('scope', 'runtime') } } @@ -248,6 +230,57 @@ the `archiveClassifier` of the shadowed JAR like the following: } ``` +## Publishing the Shadowed JAR instead of the Original JAR + +You may want to publish the shadowed JAR instead of the original JAR. This can be done by trimming +the `archiveClassifier` of the shadowed JAR like the following: + +=== "Kotlin" + + ```kotlin + plugins { + java + `maven-publish` + id("com.gradleup.shadow") + } + + tasks.shadowJar { + archiveClassifier = "" + } + + publishing { + publications { + create("shadow") + } + repositories { + maven("https://repo.myorg.com") + } + } + ``` + +=== "Groovy" + + ```groovy + plugins { + id 'java' + id 'maven-publish' + id 'com.gradleup.shadow' + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + archiveClassifier = '' + } + + publishing { + publications { + shadow(MavenPublication) + } + repositories { + maven { url = 'https://repo.myorg.com' } + } + } + ``` + ## Publishing the Shadowed Gradle Plugins The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. @@ -291,7 +324,7 @@ for details. The only thing you need to do from the Shadow side is to empty the } ``` -## Publish Custom ShadowJar Task Outputs +## Publishing Custom ShadowJar Task Outputs It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via the [`MavenPublication.artifact()`][MavenPublication.artifact] method. @@ -360,7 +393,7 @@ It is possible to publish a custom [`ShadowJar`][ShadowJar] task's output via th } ``` -## Publish the Shadowed JAR with Custom Artifact Name +## Publishing the Shadowed JAR with Custom Artifact Name It is possible to configure the artifact name of the shadowed JAR via properties like `archiveBaseName`, see more customizable properties listed in [Configuring Output Name](../configuration/README.md#configuring-output-name). e.g. From 3634e6de49e051b9c6e6344f27a95618da768edd Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 25 Aug 2025 16:20:09 +0800 Subject: [PATCH 710/941] Tweak docs about using ResourceTransformers (#1693) * Document the short syntax and the full syntax for transforming * Replace .class stuff with idiomatic Groovy call * `Closure` to closure * Merge `An instantiated instance of` part * Use `enabled` property * Fix ctor of `MyTransformer` --- docs/configuration/merging/README.md | 123 +++++++++++++++------------ 1 file changed, 67 insertions(+), 56 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 89b97421e..3a695752d 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -132,12 +132,13 @@ For simpler use cases, you can create a basic transformer: } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(MyTransformer.class) + transform(MyTransformer) } ``` -Additionally, a [`ResourceTransformer`][ResourceTransformer] can accept a `Closure` to configure the provided -[`ResourceTransformer`][ResourceTransformer]. +Additionally, a [`ResourceTransformer`][ResourceTransformer] can accept a closure to configure the provided +[`ResourceTransformer`][ResourceTransformer]. An instantiated instance of a [`ResourceTransformer`][ResourceTransformer] +can also be provided. === "Kotlin" @@ -148,16 +149,22 @@ Additionally, a [`ResourceTransformer`][ResourceTransformer] can accept a `Closu import org.gradle.api.file.FileTreeElement class MyTransformer(@get:Input var enabled: Boolean = false) : ResourceTransformer { - override fun canTransformResource(element: FileTreeElement): Boolean = true + override fun canTransformResource(element: FileTreeElement): Boolean = enabled override fun transform(context: TransformerContext) {} - override fun hasTransformedResource(): Boolean = true + override fun hasTransformedResource(): Boolean = enabled override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {} } tasks.shadowJar { + // Initialize with default constructor and configure with closure. transform() { enabled = true } + + // Or use the instantiated instance with closure. + transform(MyTransformer(false)) { + enabled = true + } } ``` @@ -171,60 +178,23 @@ Additionally, a [`ResourceTransformer`][ResourceTransformer] can accept a `Closu class MyTransformer implements ResourceTransformer { @Input boolean enabled - @Override boolean canTransformResource(FileTreeElement element) { return true } + MyTransformer(boolean enabled = false) { this.enabled = enabled } + @Override boolean canTransformResource(FileTreeElement element) { return enabled } @Override void transform(TransformerContext context) {} - @Override boolean hasTransformedResource() { return true } + @Override boolean hasTransformedResource() { return enabled } @Override void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {} } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(MyTransformer.class) { + // Initialize with default constructor and configure with closure. + transform(MyTransformer) { enabled = true } - } - ``` - -An instantiated instance of a [`ResourceTransformer`][ResourceTransformer] can also be provided. - -=== "Kotlin" - - ```kotlin - import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer - import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext - import org.apache.tools.zip.ZipOutputStream - import org.gradle.api.file.FileTreeElement - - class MyTransformer(@get:Input val enabled: Boolean) : ResourceTransformer { - override fun canTransformResource(element: FileTreeElement): Boolean = true - override fun transform(context: TransformerContext) {} - override fun hasTransformedResource(): Boolean = true - override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) {} - } - tasks.shadowJar { - transform(MyTransformer(true)) - } - ``` - -=== "Groovy" - - ```groovy - import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer - import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext - import org.apache.tools.zip.ZipOutputStream - import org.gradle.api.file.FileTreeElement - - class MyTransformer implements ResourceTransformer { - @Input final boolean enabled - MyTransformer(boolean enabled) { this.enabled = enabled } - @Override boolean canTransformResource(FileTreeElement element) { return true } - @Override void transform(TransformerContext context) {} - @Override boolean hasTransformedResource() { return true } - @Override void modifyOutputStream(ZipOutputStream os, boolean preserveFileTimestamps) {} - } - - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(new MyTransformer(true)) + // Or use the instantiated instance with closure. + transform(new MyTransformer(false)) { + enabled = true + } } ``` @@ -238,12 +208,18 @@ Multiple dependencies may use the same service descriptor file name. In this case, it is generally desired to merge the content of each instance of the file into a single output file. The [`ServiceFileTransformer`][ServiceFileTransformer] class is used to perform this merging. By default, it will merge each copy of a file under `META-INF/services` into a single file in the output JAR. +You can use either the short syntax method [`mergeServiceFiles()`][ShadowJar.mergeServiceFiles] or the full syntax +method [`transform`][ShadowJar.transform] to add the [`ServiceFileTransformer`][ServiceFileTransformer]: === "Kotlin" ```kotlin tasks.shadowJar { + // Short syntax. mergeServiceFiles() + + // Full syntax. + transform() } ``` @@ -251,12 +227,14 @@ By default, it will merge each copy of a file under `META-INF/services` into a s ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Short syntax. mergeServiceFiles() + + // Full syntax. + transform(com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer) } ``` -The above code snippet is a convenience syntax for calling `transform(ServiceFileTransformer.class)`. - > Groovy Extension Module descriptor files (located at `META-INF/services/org.codehaus.groovy.runtime.ExtensionModule`) > are ignored by the [`ServiceFileTransformer`][ServiceFileTransformer]. > This is due to these files having a different syntax than standard service descriptor files. @@ -272,9 +250,15 @@ This directory can be overridden to merge descriptor files in a different locati ```kotlin tasks.shadowJar { + // Short syntax. mergeServiceFiles { path = "META-INF/custom" } + + // Full syntax. + transform() { + path = "META-INF/custom" + } } ``` @@ -282,9 +266,15 @@ This directory can be overridden to merge descriptor files in a different locati ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Short syntax. mergeServiceFiles { path = 'META-INF/custom' } + + // Full syntax. + transform(com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer) { + path = 'META-INF/custom' + } } ``` @@ -297,9 +287,15 @@ from merging. ```kotlin tasks.shadowJar { + // Short syntax. mergeServiceFiles { exclude("META-INF/services/com.acme.*") } + + // Full syntax. + transform() { + exclude("META-INF/services/com.acme.*") + } } ``` @@ -307,9 +303,15 @@ from merging. ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Short syntax. mergeServiceFiles { exclude 'META-INF/services/com.acme.*' } + + // Full syntax. + transform(com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer) { + exclude 'META-INF/services/com.acme.*' + } } ``` @@ -324,7 +326,11 @@ The [`ShadowJar`][ShadowJar] task also provides a short syntax method to add thi ```kotlin tasks.shadowJar { + // Short syntax. mergeGroovyExtensionModules() + + // Full syntax. + transform() } ``` @@ -332,7 +338,11 @@ The [`ShadowJar`][ShadowJar] task also provides a short syntax method to add thi ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Short syntax. mergeGroovyExtensionModules() + + // Full syntax. + transform(com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer) } ``` @@ -356,7 +366,7 @@ containing Log4j 2.x Core components. It's a Gradle equivalent of ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer.class) + transform(com.github.jengelman.gradle.plugins.shadow.transformers.Log4j2PluginsCacheFileTransformer) } ``` @@ -404,7 +414,7 @@ this transformer. // short syntax append('resources/application.yml', '\n---\n') // full syntax - transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer.class) { + transform(com.github.jengelman.gradle.plugins.shadow.transformers.AppendingTransformer) { resource = 'resources/custom-config/application.yml' separator = '\n---\n' } @@ -432,7 +442,7 @@ It must be added using the [`transform`][ShadowJar.transform] methods. ```groovy tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - transform(com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer.class) { + transform(com.github.jengelman.gradle.plugins.shadow.transformers.XmlAppendingTransformer) { resource = 'properties.xml' } } @@ -453,6 +463,7 @@ It must be added using the [`transform`][ShadowJar.transform] methods. [ShadowJar.append]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html [ShadowJar.failOnDuplicateEntries]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/fail-on-duplicate-entries.html [ShadowJar.from]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action) +[ShadowJar.mergeServiceFiles]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/merge-service-files.html [ShadowJar.transform]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/transform.html [ShadowJar]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/index.html [XmlAppendingTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-xml-appending-transformer/index.html From 74651fbbdee4579ce49271bf7f57d823a03d8c62 Mon Sep 17 00:00:00 2001 From: Marcus Sawyer Date: Thu, 28 Aug 2025 03:55:14 +0100 Subject: [PATCH 711/941] Do not write modified class files for no-op relocations (#1694) * Improvement: Write class bytes unmodified if possible. If we don't need to make any changes to the class, we should use the original bytes instead of letting ASM generate new ones. Some tools use class file bytes to determine if there are classpath conflicts, and the current approach leads to false positives. Syncs with https://github.com/Goooler/maven-shade-plugin/commit/146b87b8191651fbcab0ea1ef5bdf9746f226830. * Cleanups --------- Co-authored-by: Goooler --- docs/changes/README.md | 1 + .../gradle/plugins/shadow/BasePluginTest.kt | 1 + .../gradle/plugins/shadow/RelocationTest.kt | 23 +++++++++++++++++++ .../gradle/plugins/shadow/util/JarPath.kt | 4 ++++ .../shadow/internal/RelocatorRemapper.kt | 9 ++++++++ .../plugins/shadow/tasks/ShadowCopyAction.kt | 20 +++++++++++----- 6 files changed, 52 insertions(+), 6 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 801e647b1..5e33d3360 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -12,6 +12,7 @@ ## Changed - Don't inject `TargetJvmVersion` attribute when automatic JVM targeting is disabled. ([#1666](https://github.com/GradleUp/shadow/pull/1666)) +- Do not write modified class files for no-op relocations. ([#1694](https://github.com/GradleUp/shadow/pull/1694)) ## [9.0.2](https://github.com/GradleUp/shadow/releases/tag/9.0.2) - 2025-08-15 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 9daefea28..b39baf428 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 @@ -58,6 +58,7 @@ abstract class BasePluginTest { val projectScript: Path get() = path("build.gradle") val settingsScript: Path get() = path("settings.gradle") + val outputJar: JarPath get() = jarPath("build/libs/my-1.0.jar") open val outputShadowedJar: JarPath get() = jarPath("build/libs/my-1.0-all.jar") val outputServerShadowedJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 238694223..fc466d1a8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -3,6 +3,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains +import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf import assertk.assertions.isNotEmpty import assertk.fail @@ -10,6 +11,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.Companion.CONSTANT_TIME_FOR_ZIP_ENTRIES import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsOnly +import com.github.jengelman.gradle.plugins.shadow.util.getBytes import com.github.jengelman.gradle.plugins.shadow.util.runProcess import java.net.URLClassLoader import kotlin.io.path.appendText @@ -531,6 +533,27 @@ class RelocationTest : BasePluginTest() { } } + @Test + fun classBytesUnchangedIfPossible() { + val mainClassEntry = writeClass() + projectScript.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJarTask { + enableAutoRelocation = true + } + """.trimIndent(), + ) + + run(":jar", shadowJarPath) + + val originalBytes = outputJar.use { it.getBytes(mainClassEntry) } + val relocatedBytes = outputShadowedJar.use { it.getBytes(mainClassEntry) } + assertThat(relocatedBytes).isEqualTo(originalBytes) + } + private fun writeClassWithStringRef() { writeClass { """ diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt index 1a23fe8dc..214edce89 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarPath.kt @@ -29,6 +29,10 @@ class JarPath(val path: Path) : override fun toString(): String = path.toString() } +fun ZipFile.getBytes(entryName: String): ByteArray { + return getStream(entryName).use { it.readBytes() } +} + fun ZipFile.getContent(entryName: String): String { return getStream(entryName).bufferedReader().use { it.readText() } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 232f5eccd..95f850bc9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -14,6 +14,7 @@ import org.objectweb.asm.commons.Remapper */ internal class RelocatorRemapper( private val relocators: Set, + private val onModified: () -> Unit = {}, ) : Remapper() { private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") @@ -33,6 +34,14 @@ internal class RelocatorRemapper( } private fun mapName(name: String, mapLiterals: Boolean = false): String { + val newName = mapNameImpl(name, mapLiterals) + if (newName != name) { + onModified() + } + return newName + } + + private fun mapNameImpl(name: String, mapLiterals: Boolean): String { var newName = name var prefix = "" var suffix = "" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 4dc49651b..1a77b032d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -135,8 +135,6 @@ public open class ShadowCopyAction( private inner class StreamAction( private val zipOutStr: ZipOutputStream, ) : CopyActionProcessingStreamAction { - private val remapper = RelocatorRemapper(relocators) - init { logger.info("Relocator count: ${relocators.size}.") if (encoding != null) { @@ -166,7 +164,7 @@ public open class ShadowCopyAction( } fileDetails.remapClass() } else { - val mapped = remapper.map(path) + val mapped = RelocatorRemapper(relocators).map(path) if (transform(fileDetails, mapped)) return fileDetails.writeToZip(mapped) } @@ -185,14 +183,17 @@ public open class ShadowCopyAction( * Applies remapping to the given class with the specified relocation path. The remapped class is then written * to the zip file. */ - private fun FileCopyDetails.remapClass() = file.inputStream().use { inputStream -> + private fun FileCopyDetails.remapClass() = file.readBytes().let { bytes -> + var modified = false + val remapper = RelocatorRemapper(relocators) { modified = true } + // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. // Copying the original constant pool should be avoided because it would keep references // to the original class names. This is not a problem at runtime (because these entries in the // constant pool are never used), but confuses some tools such as Felix's maven-bundle-plugin // that use the constant pool to determine the dependencies of a class. val cw = ClassWriter(0) - val cr = ClassReader(inputStream) + val cr = ClassReader(bytes) val cv = ClassRemapper(cw, remapper) try { @@ -201,6 +202,13 @@ public open class ShadowCopyAction( throw GradleException("Error in ASM processing class $path", t) } + val newBytes = if (modified) { + cw.toByteArray() + } else { + // If we didn't need to change anything, keep the original bytes as-is + bytes + } + // Temporarily remove the multi-release prefix. val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() val newPath = path.replace(multiReleasePrefix, "") @@ -211,7 +219,7 @@ public open class ShadowCopyAction( } // Now we put it back on so the class file is written out with the right extension. zipOutStr.putNextEntry(entry) - zipOutStr.write(cw.toByteArray()) + zipOutStr.write(newBytes) zipOutStr.closeEntry() } catch (_: ZipException) { logger.warn("We have a duplicate $mappedName in source project") From 619a512749a6fd7027d92aab672302c1082bfb1c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 28 Aug 2025 15:38:07 +0800 Subject: [PATCH 712/941] Add guidelines for reviewing pull requests --- .github/copilot-instructions.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..a2af306c5 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,5 @@ +Please review pull requests with the following criteria: +- **Documentation**: Carefully check the grammar, spelling, and clarity in Markdown or documentation files. Suggest improvements or rewrites for awkward phrasing. +- **Code**: Analyze all code changes for bugs, maintainability, and adherence to project conventions. Highlight any potential issues or improvements. +- **Coverage**: Ensure that new features or changes include appropriate tests and documentation. Request missing items. +- **Feedback**: Phrase suggestions and feedback in a constructive, respectful, and actionable way. From 53203e1b307fc5555abe46512bfc7bbea93f06d5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 28 Aug 2025 16:25:57 +0800 Subject: [PATCH 713/941] Document more about eachFile, filesMatching, and filesNotMatching (#1682) * Mention `eachFile` and `filesNotMatching` * New steps * Use `ParameterizedTest` for `strategyCanBeOverriddenByEachFile` * Cleanups * Add more examples * Merge duplicate steps * Add the optional step at the last --- docs/configuration/merging/README.md | 113 ++++++++++++++++-- .../ServiceFileTransformerTest.kt | 63 +++++++--- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 15 +-- 3 files changed, 158 insertions(+), 33 deletions(-) diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 3a695752d..f332acd8a 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -77,20 +77,117 @@ Different strategies will lead to different results for `foo/bar` files in the J ``` The [`ResourceTransformer`][ResourceTransformer]s like [`ServiceFileTransformer`][ServiceFileTransformer] will not work -as expected as the duplicate resource files fed for them are excluded beforehand. However, this behavior might be what you expected for duplicate `foo/bar` files, preventing them from being included. +as expected as the duplicate resource files fed for them are excluded beforehand. However, this behavior might be what +you expected for duplicate `foo/bar` files, preventing them from being included. -Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several steps -to take: +Want [`ResourceTransformer`][ResourceTransformer]s and `duplicatesStrategy` to work together? There are several common +steps to take: -1. Set the strategy to `INCLUDE` or `WARN`. +1. Set the default strategy to `INCLUDE` or `WARN`. 2. Apply your [`ResourceTransformer`][ResourceTransformer]s. 3. Remove duplicate entries by - - overriding the default strategy for specific files using [`filesMatching`][Jar.filesMatching] + - overriding the default strategy for specific files to `EXCLUDE` or `FAIL` using + [`filesMatching`][Jar.filesMatching], [`filesNotMatching`][Jar.filesNotMatching], or [`eachFile`][Jar.eachFile] functions - or applying [`PreserveFirstFoundResourceTransformer`][PreserveFirstFoundResourceTransformer] for specific files - or write your own [`ResourceTransformer`][ResourceTransformer] to handle duplicates - or mechanism similar. -4. Optionally, enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. -5. Optionally, use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. + +Alternatively, you can follow these steps: + +1. Set the default strategy to `EXCLUDE` or `FAIL`. +2. Apply your [`ResourceTransformer`][ResourceTransformer]s. +3. Bypass the duplicate entries which should be handled by the [`ResourceTransformer`][ResourceTransformer]s using + [`filesMatching`][Jar.filesMatching], [`filesNotMatching`][Jar.filesNotMatching], or [`eachFile`][Jar.eachFile] functions + to set their `duplicatesStrategy` to `INCLUDE` or `WARN`. + +Optional steps: + +- Enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. +- Use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. + +Here are some examples: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + // Step 2. + mergeServiceFiles() + // Step 3. Using `filesNotMatching`: + filesNotMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + } + // Step 3. Using `PreserveFirstFoundResourceTransformer`: + transform() { + resources.add("META-INF/foo/**") // Or something else where the first occurrence should be preserved. + } + } + + tasks.shadowJar { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + // Step 2. + mergeServiceFiles() + // Step 3. Using `filesMatching`: + filesMatching("META-INF/services/**") { + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + // Step 3. Using `eachFile`: + eachFile { + if (path.startsWith("META-INF/services/")) { + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + } + } + + tasks.shadowJar { + // Optional step. + failOnDuplicateEntries = true + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + // Step 2. + mergeServiceFiles() + // Step 3. Using `filesNotMatching`: + filesNotMatching('META-INF/services/**') { + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + } + // Step 3. Using `PreserveFirstFoundResourceTransformer`: + transform(com.github.jengelman.gradle.plugins.shadow.transformers.PreserveFirstFoundResourceTransformer) { + resources.add('META-INF/foo/**') // Or something else where the first occurrence should be preserved. + } + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Step 1. + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Or FAIL. + // Step 2. + mergeServiceFiles() + // Step 3. Using `filesMatching`: + filesMatching('META-INF/services/**') { + duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + // Step 3. Using `eachFile`: + eachFile { + if (it.path.startsWith('META-INF/services/')) { + it.duplicatesStrategy = DuplicatesStrategy.INCLUDE // Or WARN. + } + } + } + + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + // Optional step. + failOnDuplicateEntries = true + } + ``` ## Basic ResourceTransformer Usage @@ -451,7 +548,9 @@ It must be added using the [`transform`][ShadowJar.transform] methods. [AbstractCopyTask]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html +[Jar.eachFile]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:eachFile(org.gradle.api.Action) [Jar.filesMatching]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:filesMatching(java.lang.Iterable,%20org.gradle.api.Action) +[Jar.filesNotMatching]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:filesNotMatching(java.lang.Iterable,%20org.gradle.api.Action) [AppendingTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-appending-transformer/index.html [DuplicatesStrategy]: https://docs.gradle.org/current/javadoc/org/gradle/api/file/DuplicatesStrategy.html [GroovyExtensionModuleTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-groovy-extension-module-transformer/index.html diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index edc224d9e..6b1d2fe41 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -8,6 +8,11 @@ import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.writeText import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.file.DuplicatesStrategy.EXCLUDE +import org.gradle.api.file.DuplicatesStrategy.FAIL +import org.gradle.api.file.DuplicatesStrategy.INCLUDE +import org.gradle.api.file.DuplicatesStrategy.INHERIT +import org.gradle.api.file.DuplicatesStrategy.WARN import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -221,8 +226,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } @Test - fun strategyExcludeCanBeOverriddenByFilesMatching() { - writeDuplicatesStrategy(DuplicatesStrategy.EXCLUDE) + fun strategyCanBeOverriddenByFilesMatching() { + writeDuplicatesStrategy(EXCLUDE) projectScript.appendText( """ $shadowJarTask { @@ -242,8 +247,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } @Test - fun strategyIncludeCanBeOverriddenByFilesNotMatching() { - writeDuplicatesStrategy(DuplicatesStrategy.INCLUDE) + fun strategyCanBeOverriddenByFilesNotMatching() { + writeDuplicatesStrategy(INCLUDE) projectScript.appendText( """ $shadowJarTask { @@ -262,6 +267,34 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } + @ParameterizedTest + @MethodSource("eachFileStrategyProvider") + fun strategyCanBeOverriddenByEachFile( + default: DuplicatesStrategy, + override: DuplicatesStrategy, + matchPath: String, + ) { + writeDuplicatesStrategy(default) + projectScript.appendText( + """ + $shadowJarTask { + eachFile { + if (path == '$matchPath') { + duplicatesStrategy = DuplicatesStrategy.$override + } + } + } + """.trimIndent(), + ) + + run(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) + getContent(ENTRY_SERVICES_FOO).isEqualTo("one") + } + } + private fun writeDuplicatesStrategy(strategy: DuplicatesStrategy) { projectScript.appendText( """ @@ -279,21 +312,21 @@ class ServiceFileTransformerTest : BaseTransformerTest() { private companion object { @JvmStatic fun withThrowingProvider() = listOf( - Arguments.of( - DuplicatesStrategy.FAIL, - "Cannot copy zip entry .* to .* because zip entry .* has already been copied there", - ), - Arguments.of( - DuplicatesStrategy.INHERIT, - "Entry .* is a duplicate but no duplicate handling strategy has been set", - ), + Arguments.of(FAIL, "Cannot copy zip entry .* to .* because zip entry .* has already been copied there"), + Arguments.of(INHERIT, "Entry .* is a duplicate but no duplicate handling strategy has been set"), ) @JvmStatic fun withoutThrowingProvider() = listOf( - Arguments.of(DuplicatesStrategy.EXCLUDE, CONTENT_ONE, "one"), - Arguments.of(DuplicatesStrategy.INCLUDE, CONTENT_ONE_TWO, "one\ntwo"), - Arguments.of(DuplicatesStrategy.WARN, CONTENT_ONE_TWO, "one\ntwo"), + Arguments.of(EXCLUDE, CONTENT_ONE, "one"), + Arguments.of(INCLUDE, CONTENT_ONE_TWO, "one\ntwo"), + Arguments.of(WARN, CONTENT_ONE_TWO, "one\ntwo"), + ) + + @JvmStatic + fun eachFileStrategyProvider() = listOf( + Arguments.of(EXCLUDE, INCLUDE, ENTRY_SERVICES_SHADE), + Arguments.of(INCLUDE, EXCLUDE, ENTRY_SERVICES_FOO), ) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index be28cf93c..cbe130cc9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -219,19 +219,12 @@ public abstract class ShadowJar : Jar() { * **NOTE:** The strategy takes precedence over transforming and relocating. * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to * [EXCLUDE] (the default), as the duplicate resource files fed for them are excluded beforehand. - * Want [ResourceTransformer]s and the strategy to work together? There are several steps to take: - * - * 1. Set the strategy to [INCLUDE] or [WARN]. - * 2. Apply your [ResourceTransformer]s. - * 3. Remove duplicate entries by - * - overriding the default strategy for specific files using [filesMatching] - * - or applying `PreserveFirstFoundResourceTransformer` for specific files - * - or write your own `ResourceTransformer`s to handle duplicates - * - or mechanism similar. - * 4. Optionally, enable [failOnDuplicateEntries] to check duplicate entries in the final JAR. - * 5. Optionally, use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. + * Want [ResourceTransformer]s and the strategy to work together? See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. * + * @see [eachFile] * @see [filesMatching] + * @see [filesNotMatching] * @see [DuplicatesStrategy] * @see [CopySpec.duplicatesStrategy] */ From e4567fad6e4cff665ecf8cd2f280e518d38efdbb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 28 Aug 2025 18:31:08 +0800 Subject: [PATCH 714/941] Prefer using when in visitFile (#1697) --- .../plugins/shadow/tasks/ShadowCopyAction.kt | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 1a77b032d..d28a37fc3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -156,17 +156,20 @@ public open class ShadowCopyAction( private fun visitFile(fileDetails: FileCopyDetails) { val path = fileDetails.path - if (path.endsWith(".class")) { - if (isUnused(path)) return - if (relocators.isEmpty()) { - fileDetails.writeToZip(path) - return + when { + path.endsWith(".class") -> { + if (isUnused(path)) return + if (relocators.isEmpty()) { + fileDetails.writeToZip(path) + } else { + fileDetails.remapClass() + } + } + else -> { + val mapped = RelocatorRemapper(relocators).map(path) + if (transform(fileDetails, mapped)) return + fileDetails.writeToZip(mapped) } - fileDetails.remapClass() - } else { - val mapped = RelocatorRemapper(relocators).map(path) - if (transform(fileDetails, mapped)) return - fileDetails.writeToZip(mapped) } } From 872445153b14cf1e54276bfe2a14564bb812a76e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 29 Aug 2025 16:40:07 +0800 Subject: [PATCH 715/941] Simplify SimpleRelocator.hashCode (#1698) --- .../shadow/relocation/SimpleRelocator.kt | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 1ae34df27..931580927 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.relocation +import java.util.Objects import java.util.regex.Pattern import org.apache.commons.io.FilenameUtils import org.codehaus.plexus.util.SelectorUtils @@ -149,19 +150,18 @@ public open class SimpleRelocator @JvmOverloads constructor( excludes == other.excludes } - override fun hashCode(): Int { - var result = rawString.hashCode() - result = 31 * result + skipStringConstants.hashCode() - result = 31 * result + pattern.hashCode() - result = 31 * result + pathPattern.hashCode() - result = 31 * result + shadedPattern.hashCode() - result = 31 * result + shadedPathPattern.hashCode() - result = 31 * result + sourcePackageExcludes.hashCode() - result = 31 * result + sourcePathExcludes.hashCode() - result = 31 * result + includes.hashCode() - result = 31 * result + excludes.hashCode() - return result - } + override fun hashCode(): Int = Objects.hash( + rawString, + skipStringConstants, + pattern, + pathPattern, + shadedPattern, + shadedPathPattern, + sourcePackageExcludes, + sourcePathExcludes, + includes, + excludes, + ) private fun isIncluded(path: String): Boolean { return includes.isEmpty() || includes.any { SelectorUtils.matchPath(it, path, "/", true) } From e84097303fd699ea091985ebd83e20a44e2951c4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 29 Aug 2025 17:34:02 +0800 Subject: [PATCH 716/941] Apply kotlin-reflect explicitly (#1699) Code highlighting for https://github.com/GradleUp/shadow/blob/53203e1b307fc5555abe46512bfc7bbea93f06d5/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt#L76-L80 --- build.gradle.kts | 1 + gradle/libs.versions.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 04359157a..d7be3d25f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -105,6 +105,7 @@ val testGradleVersion: String = providers.gradleProperty("testGradleVersion").or dependencies { compileOnly(libs.develocity) compileOnly(libs.kotlin.kmp) + compileOnly(libs.kotlin.reflect) api(libs.apache.ant) // Types from Ant are exposed in the public API. implementation(libs.apache.commonsIo) implementation(libs.apache.log4j) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 366abf969..b77f7a0a1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,6 +13,7 @@ asm = "org.ow2.asm:asm-commons:9.8" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.13" jdom2 = "org.jdom:jdom2:2.0.6.1" +kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" xmlunit = "org.xmlunit:xmlunit-legacy:2.10.3" From 6e3497d3417f633931a347f75585f62155d1bb25 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 29 Aug 2025 17:46:27 +0800 Subject: [PATCH 717/941] Prepare version 9.1.0 --- docs/changes/README.md | 3 +-- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 5e33d3360..026e7d248 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.0.2...HEAD) - 2025-xx-xx +## [9.1.0](https://github.com/GradleUp/shadow/compare/9.1.0) - 2025-08-29 ## Added @@ -14,7 +14,6 @@ - Don't inject `TargetJvmVersion` attribute when automatic JVM targeting is disabled. ([#1666](https://github.com/GradleUp/shadow/pull/1666)) - Do not write modified class files for no-op relocations. ([#1694](https://github.com/GradleUp/shadow/pull/1694)) - ## [9.0.2](https://github.com/GradleUp/shadow/releases/tag/9.0.2) - 2025-08-15 ### Fixed diff --git a/gradle.properties b/gradle.properties index 1dd6be137..83dd8cd54 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.0.3-SNAPSHOT +VERSION_NAME=9.1.0 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From ba18d5304c44ee7619183cc5ee493fb746fdad34 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 29 Aug 2025 17:48:07 +0800 Subject: [PATCH 718/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 026e7d248..05092fb79 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.1.0...HEAD) - 2025-xx-xx + + ## [9.1.0](https://github.com/GradleUp/shadow/compare/9.1.0) - 2025-08-29 ## Added diff --git a/gradle.properties b/gradle.properties index 83dd8cd54..250d4ba54 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,7 +22,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.1.0 +VERSION_NAME=9.1.1-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 5ca361f5cdd801df936e2fba91e343088a1c33ff Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 30 Aug 2025 19:03:50 +0800 Subject: [PATCH 719/941] Update plugin android-lint to v8.12.2 (#1702) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b77f7a0a1..7c6f1ee05 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,7 +34,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.12.1" +android-lint = "com.android.lint:8.12.2" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From e687c7462d27db62df16187cc6762bd192eb6e70 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 31 Aug 2025 16:47:02 +0800 Subject: [PATCH 720/941] Clarify the minimization behavior for projects (#1703) * Document minimizing sections a bit * Mark issue 744 for `excludeProjectFromMinimize` * Update docs/configuration/minimizing/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/configuration/minimizing/README.md | 14 +++++++++----- .../gradle/plugins/shadow/MinimizeTest.kt | 3 +++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/configuration/minimizing/README.md b/docs/configuration/minimizing/README.md index 897b815c8..91ab66b5d 100644 --- a/docs/configuration/minimizing/README.md +++ b/docs/configuration/minimizing/README.md @@ -1,7 +1,7 @@ # Minimizing -Shadow can automatically remove all classes of dependencies that are not used by the project, thereby minimizing the -resulting shadowed JAR. +Shadow can automatically remove all JARs and classes of dependencies that are not used by the project, thereby +minimizing the resulting shadowed JAR. === "Kotlin" @@ -47,7 +47,7 @@ a `dependency` is interpreted as a regular expression. > Dependencies scoped as `api` will be automatically excluded from minimization and used as "entry points" on > minimization. -Similar to dependencies, projects can also be excluded. +Similar to [`ShadowJar.dependencies`][ShadowJar.dependencies], projects can also be excluded. === "Kotlin" @@ -69,5 +69,9 @@ Similar to dependencies, projects can also be excluded. } ``` -> When excluding a `project`, all dependencies of the excluded `project` are automatically -> excluded as well. +> When excluding a `project`, all dependencies of the excluded `project` are automatically excluded from +> minimization as well. + + + +[ShadowJar.dependencies]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/dependencies.html diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index 108305efa..b0fda4b83 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -140,6 +140,9 @@ class MinimizeTest : BasePluginTest() { * 'Client', 'Server' and 'junit' are independent. * Unused classes of 'client' and theirs dependencies shouldn't be removed. */ + @Issue( + "https://github.com/GradleUp/shadow/issues/744", + ) @Test fun excludeProjectFromMinimize() { writeClientAndServerModules( From d6347798dbfcb9f17c8fe6a94e78c88171a8cd67 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 1 Sep 2025 15:45:54 +0800 Subject: [PATCH 721/941] Merge Gradle Module descriptors into the modern META-INF path (#1706) * Comment Module descriptor https://groovy-lang.org/metaprogramming.html#module-descriptor * Add `GroovyResourceTransformer` https://github.com/apache/maven-shade-plugin/blob/3409e5aed8b10d04f2de63af835c52058a834848/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java * Merge them and reduce legacy logic * Test `mergeLegacyAndModernModuleDescriptorsIntoTheNewResourcePath` * Use `ParameterizedTest` * Update changelog * Update docs/changes/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Cleanups --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 5 +++ .../GroovyExtensionModuleTransformerTest.kt | 31 +++++++++++----- .../GroovyExtensionModuleTransformer.kt | 37 +++++++------------ 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 05092fb79..9fbe75076 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,11 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.1.0...HEAD) - 2025-xx-xx +### Changed + +- Merge Gradle Module descriptors into the modern `META-INF` path. ([#1706](https://github.com/GradleUp/shadow/pull/1706)) + The Gradle Module descriptors (`org.codehaus.groovy.runtime.ExtensionModule` files) defined under `META-INF/services/` + and `META-INF/groovy` will be merged into `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`. ## [9.1.0](https://github.com/GradleUp/shadow/compare/9.1.0) - 2025-08-29 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index 5ed589c12..898b8df69 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -13,8 +13,9 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo import com.github.jengelman.gradle.plugins.shadow.util.getContent import java.nio.file.Path import kotlin.io.path.appendText -import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.ValueSource class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { @@ -39,23 +40,27 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { run(shadowJarPath) - commonAssertions(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) + commonAssertions() } - @Test - fun groovyExtensionModuleTransformerWorksForLegacyGroovy() { + @ParameterizedTest + @MethodSource("resourcePathProvider") + fun mergeLegacyAndModernModuleDescriptorsIntoTheNewResourcePath( + fooEntry: String, + barEntry: String, + ) { projectScript.appendText( transform( dependenciesBlock = implementationFiles( - buildJarFoo(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), - buildJarBar(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + buildJarFoo(fooEntry), + buildJarBar(barEntry), ), ), ) run(shadowJarPath) - commonAssertions(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR) + commonAssertions() } private fun buildJarFoo( @@ -86,8 +91,8 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { ) } - private fun commonAssertions(entry: String) { - val properties = outputShadowedJar.use { it.getContent(entry) }.toProperties() + private fun commonAssertions() { + val properties = outputShadowedJar.use { it.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) }.toProperties() assertThat(properties.getProperty(KEY_MODULE_NAME)).isEqualTo(MERGED_MODULE_NAME) assertThat(properties.getProperty(KEY_MODULE_VERSION)).isEqualTo(MERGED_MODULE_VERSION) @@ -102,5 +107,13 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { const val EXTENSION_CLASSES_BAR = "com.acme.bar.SomeExtension,com.acme.bar.AnotherExtension" const val STATIC_EXTENSION_CLASSES_FOO = "com.acme.foo.FooStaticExtension" const val STATIC_EXTENSION_CLASSES_BAR = "com.acme.bar.SomeStaticExtension" + + @JvmStatic + fun resourcePathProvider() = listOf( + Arguments.of(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + Arguments.of(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + Arguments.of(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + Arguments.of(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), + ) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index 887e485fb..885dfcf67 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -21,26 +21,15 @@ import org.gradle.api.file.FileTreeElement * files came from the legacy location, otherwise it will be written into the now standard location (META-INF/groovy). * Note that certain JDK9+ tooling will break when using the legacy location. * - * Modified from [eu.appsatori.gradle.fatjar.tasks.PrepareFiles.groovy](https://github.com/musketyr/gradle-fatjar-plugin/blob/master/src/main/groovy/eu/appsatori/gradle/fatjar/tasks/PrepareFiles.groovy). * Related to [org.apache.maven.plugins.shade.resource.GroovyResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java). */ @CacheableTransformer public open class GroovyExtensionModuleTransformer : ResourceTransformer { private val module = Properties() - /** - * default to Groovy 2.4 or earlier - */ - private var legacy = true - override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.path - if (path == PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) { - // Groovy 2.5+ - legacy = false - return true - } - return path == PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR + return path == PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR || path == PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR } override fun transform(context: TransformerContext) { @@ -64,26 +53,28 @@ public open class GroovyExtensionModuleTransformer : ResourceTransformer { } } - private fun handle(key: String, value: String, mergeValue: (String) -> Unit) { - val existingValue = module.getProperty(key) - if (existingValue != null) { - mergeValue(existingValue) - } else { - module.setProperty(key, value) - } - } - override fun hasTransformedResource(): Boolean = module.isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val name = if (legacy) PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR else PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR - os.putNextEntry(zipEntry(name, preserveFileTimestamps)) + os.putNextEntry(zipEntry(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, preserveFileTimestamps)) module.inputStream().use { it.copyTo(os) } os.closeEntry() } + private fun handle(key: String, value: String, mergeValue: (String) -> Unit) { + val existingValue = module.getProperty(key) + if (existingValue != null) { + mergeValue(existingValue) + } else { + module.setProperty(key, value) + } + } + + /* + * https://groovy-lang.org/metaprogramming.html#module-descriptor + */ public companion object { public const val PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR: String = "META-INF/services/org.codehaus.groovy.runtime.ExtensionModule" From a5f61281c88a3978cd5451bf122de5b42e48fee9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 2 Sep 2025 12:36:25 +0800 Subject: [PATCH 722/941] Comments for afterEvaluate usages (#1707) * Try to remove `afterEvaluate` * Revert "Try to remove `afterEvaluate`" This reverts commit 9aa3bc58f31defebe0fcc6bc2e317877f4c63108. * Comments --- .../github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 2b5088c5b..b098c9a19 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 @@ -67,7 +67,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( it.outgoing.artifact(tasks.shadowJar) } - // Must use afterEvaluate here as we need to check the value of targetJvmVersion and track its changes. + // Must use afterEvaluate here as we need to track the changes of addTargetJvmVersionAttribute. afterEvaluate { if (shadow.addTargetJvmVersionAttribute.get()) { logger.info("Setting ${TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name} attribute for ${shadowRuntimeElements.name} configuration.") @@ -98,6 +98,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> variant.mapToMavenScope("runtime") } + // Must use afterEvaluate here as we need to track the changes of addShadowVariantIntoJavaComponent. afterEvaluate { if (shadow.addShadowVariantIntoJavaComponent.get()) { logger.info("Adding ${shadowRuntimeElements.name} variant to Java component.") From 8260739dec7d9bb5564bce315da36044ed4d7fc9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 2 Sep 2025 13:07:46 +0800 Subject: [PATCH 723/941] Prefer kotlin over kt Updated code block syntax highlighting from 'kt' to 'kotlin' for better readability. --- docs/changes/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 9fbe75076..0eeaef73a 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -210,7 +210,7 @@ **8.x** -```kt +```kotlin tasks.shadowJar { isEnableRelocation = true duplicatesStrategy = DuplicatesStrategy.EXCLUDE @@ -221,7 +221,7 @@ tasks.shadowJar { **9.x** -```kt +```kotlin tasks.shadowJar { // `isEnableRelocation` has been renamed to `enableAutoRelocation`. enableAutoRelocation = true From 575fece632565f1aecddb5b3e03cac238a578b61 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 2 Sep 2025 17:34:59 +0800 Subject: [PATCH 724/941] Add more snippets for the version 9.1.0 (#1708) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 0eeaef73a..9f7352fce 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -11,13 +11,38 @@ ## [9.1.0](https://github.com/GradleUp/shadow/compare/9.1.0) - 2025-08-29 -## Added +### Added - Allow opting out of `shadowRuntimeElements` variant. ([#1662](https://github.com/GradleUp/shadow/pull/1662)) + ```kotlin + shadow { + // Disable publishing `shadowRuntimeElements` as an optional variant of the `java` component. + addShadowVariantIntoJavaComponent = false + } + + // configuration must be done in the `afterEvaluate` phase, you cannot access `shadowRuntimeElements` before that. + val javaComponent = components["java"] as AdhocComponentWithVariants + javaComponent.withVariantsFromConfiguration(configurations["shadowRuntimeElements"]) { + // See more details in https://github.com/GradleUp/shadow/pull/1662. + skip() + } + ``` - Allow opting out of `TARGET_JVM_VERSION_ATTRIBUTE`. ([#1674](https://github.com/GradleUp/shadow/pull/1674)) + ```kotlin + shadow { + // Disable adding `TargetJvmVersion` attribute into the Gradle Module Metadata of the shadowed jar. + addTargetJvmVersionAttribute = false + } + ``` - Allow opting out of `Multi-Release` attribute. ([#1675](https://github.com/GradleUp/shadow/pull/1675)) + ```kotlin + tasks.shadowJar { + // Disable adding `Multi-Release` attribute into the manifest of the shadowed jar. + addMultiReleaseAttribute = false + } + ``` -## Changed +### Changed - Don't inject `TargetJvmVersion` attribute when automatic JVM targeting is disabled. ([#1666](https://github.com/GradleUp/shadow/pull/1666)) - Do not write modified class files for no-op relocations. ([#1694](https://github.com/GradleUp/shadow/pull/1694)) From 05a47b55ba420faea46e3d1ff61c9d39a3b58700 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 2 Sep 2025 17:44:28 +0800 Subject: [PATCH 725/941] Add extensions for Iterable (#1710) * New `CompositeRelocator` * Remove `RelocatorRemapper.mapPath` * Cleanups * Replace `CompositeRelocator` with `relocatePath` extension * Add `relocateClass` extension * Cleanups * Update changelog --- api/shadow.api | 2 ++ docs/changes/README.md | 4 +++ .../shadow/internal/RelocatorRemapper.kt | 5 ---- .../shadow/relocation/RelocationContext.kt | 18 +++++++++++++ .../plugins/shadow/tasks/ShadowCopyAction.kt | 17 ++++++------ .../ComponentsXmlResourceTransformer.kt | 26 +++++++------------ .../Log4j2PluginsCacheFileTransformer.kt | 6 +---- .../transformers/ServiceFileTransformer.kt | 21 +++------------ 8 files changed, 47 insertions(+), 52 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index cf28c3d2c..c983f9eaf 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -106,7 +106,9 @@ public final class com/github/jengelman/gradle/plugins/shadow/relocation/Relocat public final class com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContextKt { public static final fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;Ljava/lang/String;)Ljava/lang/String; + public static final fun relocateClass (Ljava/lang/Iterable;Ljava/lang/String;)Ljava/lang/String; public static final fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/Relocator;Ljava/lang/String;)Ljava/lang/String; + public static final fun relocatePath (Ljava/lang/Iterable;Ljava/lang/String;)Ljava/lang/String; } public abstract interface class com/github/jengelman/gradle/plugins/shadow/relocation/Relocator { diff --git a/docs/changes/README.md b/docs/changes/README.md index 9f7352fce..8c9389a31 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.1.0...HEAD) - 2025-xx-xx +### Added + +- Add extensions for `Iterable`. ([#1710](https://github.com/GradleUp/shadow/pull/1710)) + ### Changed - Merge Gradle Module descriptors into the modern `META-INF` path. ([#1706](https://github.com/GradleUp/shadow/pull/1706)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 95f850bc9..61f824852 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -28,11 +28,6 @@ internal class RelocatorRemapper( override fun map(internalName: String): String = mapName(internalName) - fun mapPath(path: String): String { - val dotIndex = path.indexOf('.') - return if (dotIndex == -1) path else map(path.take(dotIndex)) - } - private fun mapName(name: String, mapLiterals: Boolean = false): String { val newName = mapNameImpl(name, mapLiterals) if (newName != name) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt index b3c5c628a..00fcc90ac 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt @@ -15,3 +15,21 @@ public fun Relocator.relocateClass(className: String): String { public fun Relocator.relocatePath(path: String): String { return relocatePath(RelocatePathContext(path)) } + +public fun Iterable.relocateClass(className: String): String { + forEach { relocator -> + if (relocator.canRelocateClass(className)) { + return relocator.relocateClass(className) + } + } + return className +} + +public fun Iterable.relocatePath(path: String): String { + forEach { relocator -> + if (relocator.canRelocatePath(path)) { + return relocator.relocatePath(path) + } + } + return path +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index d28a37fc3..5587f1df3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper import com.github.jengelman.gradle.plugins.shadow.internal.cast import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.relocation.relocatePath import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import java.io.File @@ -166,9 +167,9 @@ public open class ShadowCopyAction( } } else -> { - val mapped = RelocatorRemapper(relocators).map(path) - if (transform(fileDetails, mapped)) return - fileDetails.writeToZip(mapped) + val relocated = relocators.relocatePath(path) + if (transform(fileDetails, relocated)) return + fileDetails.writeToZip(relocated) } } } @@ -215,9 +216,9 @@ public open class ShadowCopyAction( // Temporarily remove the multi-release prefix. val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() val newPath = path.replace(multiReleasePrefix, "") - val mappedName = multiReleasePrefix + remapper.mapPath(newPath) + val relocatedPath = multiReleasePrefix + relocators.relocatePath(newPath) try { - val entry = zipEntry("$mappedName.class", preserveFileTimestamps, lastModified) { + val entry = zipEntry(relocatedPath, preserveFileTimestamps, lastModified) { unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() } // Now we put it back on so the class file is written out with the right extension. @@ -225,16 +226,16 @@ public open class ShadowCopyAction( zipOutStr.write(newBytes) zipOutStr.closeEntry() } catch (_: ZipException) { - logger.warn("We have a duplicate $mappedName in source project") + logger.warn("We have a duplicate $relocatedPath in source project") } } - private fun transform(fileDetails: FileCopyDetails, mapped: String): Boolean { + private fun transform(fileDetails: FileCopyDetails, path: String): Boolean { val transformer = transformers.find { it.canTransformResource(fileDetails) } ?: return false fileDetails.file.inputStream().use { inputStream -> transformer.transform( TransformerContext( - path = mapped, + path = path, inputStream = inputStream, relocators = relocators, ), diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index 9a4b731d3..a28e0e68e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -63,14 +63,16 @@ public open class ComponentsXmlResourceTransformer : ResourceTransformer { val children = newDom.getChild("components").getChildren("component") for (component in children) { - var role: String? = getValue(component, "role") - role = getRelocatedClass(role, context) + val role = getValue(component, "role").let { + context.relocators.relocateClass(it) + } setValue(component, "role", role) val roleHint = getValue(component, "role-hint") - var impl: String? = getValue(component, "implementation") - impl = getRelocatedClass(impl, context) + val impl = getValue(component, "implementation").let { + context.relocators.relocateClass(it) + } setValue(component, "implementation", impl) val key = "$role:$roleHint" @@ -84,8 +86,9 @@ public open class ComponentsXmlResourceTransformer : ResourceTransformer { if (requirements != null && requirements.childCount > 0) { for (r in requirements.childCount - 1 downTo 0) { val requirement = requirements.getChild(r) - var requiredRole: String? = getValue(requirement, "role") - requiredRole = getRelocatedClass(requiredRole, context) + val requiredRole = getValue(requirement, "role").let { + context.relocators.relocateClass(it) + } setValue(requirement, "role", requiredRole) } } @@ -105,17 +108,6 @@ public open class ComponentsXmlResourceTransformer : ResourceTransformer { public companion object { public const val COMPONENTS_XML_PATH: String = "META-INF/plexus/components.xml" - private fun getRelocatedClass(className: String?, context: TransformerContext): String? { - if (!className.isNullOrEmpty()) { - for (relocator in context.relocators) { - if (relocator.canRelocateClass(className)) { - return relocator.relocateClass(className) - } - } - } - return className - } - private fun getValue(dom: Xpp3Dom, element: String): String { return dom.getChild(element).value.orEmpty() } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index 551ffd7a9..e3ec52983 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -67,11 +67,7 @@ public open class Log4j2PluginsCacheFileTransformer : ResourceTransformer { internal fun relocatePlugins(pluginCache: PluginCache) { pluginCache.allCategories.values.forEach { currentMap -> currentMap.values.forEach { currentPluginEntry -> - val className = currentPluginEntry.className - tempRelocators.firstOrNull { it.canRelocateClass(className) }?.let { relocator -> - // Then we perform that relocation and update the plugin entry to reflect the new value. - currentPluginEntry.className = relocator.relocateClass(className) - } + currentPluginEntry.className = tempRelocators.relocateClass(currentPluginEntry.className) } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index dbd7627e6..87e888583 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -45,24 +45,11 @@ public open class ServiceFileTransformer( } override fun transform(context: TransformerContext) { - var resource = context.path.substringAfter("$path/") - context.relocators.forEach { relocator -> - if (relocator.canRelocateClass(resource)) { - resource = relocator.relocateClass(resource) - return@forEach - } - } - resource = "$path/$resource" + val resource = path + "/" + + context.relocators.relocateClass(context.path.substringAfter("$path/")) val out = serviceEntries.getOrPut(resource) { mutableSetOf() } - - context.inputStream.bufferedReader().use { it.readLines() }.forEach { - var line = it - context.relocators.forEach { relocator -> - if (relocator.canRelocateClass(line)) { - line = relocator.relocateClass(line) - } - } - out.add(line) + context.inputStream.bufferedReader().use { it.readLines() }.forEach { line -> + out.add(context.relocators.relocateClass(line)) } } From e8df109bfff962487cb817231205af12aab56004 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 2 Sep 2025 17:48:57 +0800 Subject: [PATCH 726/941] Remove the suppression for JavaMapForEach --- .../shadow/transformers/GroovyExtensionModuleTransformer.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index 885dfcf67..064b9b3dc 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -35,8 +35,7 @@ public open class GroovyExtensionModuleTransformer : ResourceTransformer { override fun transform(context: TransformerContext) { val props = Properties() props.load(context.inputStream) - @Suppress("JavaMapForEach") - props.forEach { key, value -> + props.forEach { (key, value) -> when (key as String) { KEY_MODULE_NAME -> handle(key, value as String) { module.setProperty(key, MERGED_MODULE_NAME) From 2ac0f1dd2593d20bc4aced9d44f1677ec29776c6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 2 Sep 2025 18:45:31 +0800 Subject: [PATCH 727/941] Support relocating Groovy extensions in Module descriptors (#1705) * Relocate `extensionClasses` and `staticExtensionClasses` * Update changelog * Relocate `extensionModulePath` * Test `groovyExtensionModuleTransformerWithRelocation` * Revert "Relocate `extensionModulePath`" This reverts commit 19be498e * Cleanup --- docs/changes/README.md | 1 + .../GroovyExtensionModuleTransformerTest.kt | 37 ++++++++++++++++++- .../GroovyExtensionModuleTransformer.kt | 10 ++++- 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 8c9389a31..2a417fbae 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -5,6 +5,7 @@ ### Added +- Support relocating Groovy extensions in Module descriptors. ([#1705](https://github.com/GradleUp/shadow/pull/1705)) - Add extensions for `Iterable`. ([#1710](https://github.com/GradleUp/shadow/pull/1710)) ### Changed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index 898b8df69..2abdc7d52 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -10,9 +10,11 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_VERSION import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR +import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.getContent import java.nio.file.Path import kotlin.io.path.appendText +import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource @@ -63,6 +65,35 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { commonAssertions() } + @Test + fun groovyExtensionModuleTransformerWithRelocation() { + projectScript.appendText( + """ + dependencies { + ${implementationFiles(buildJarFoo(), buildJarBar())} + } + $shadowJarTask { + relocate('com.acme', 'com.example.shaded.acme') + mergeGroovyExtensionModules() + } + """.trimIndent(), + ) + + run(shadowJarPath) + + val properties = outputShadowedJar.extensionModuleProperties + + assertThat(properties.getProperty(KEY_MODULE_NAME)).isEqualTo(MERGED_MODULE_NAME) + assertThat(properties.getProperty(KEY_MODULE_VERSION)).isEqualTo(MERGED_MODULE_VERSION) + assertThat(properties.getProperty(KEY_EXTENSION_CLASSES)) + .isEqualTo( + "com.example.shaded.acme.foo.FooExtension,com.example.shaded.acme.foo.BarExtension," + + "com.example.shaded.acme.bar.SomeExtension,com.example.shaded.acme.bar.AnotherExtension", + ) + assertThat(properties.getProperty(KEY_STATIC_EXTENSION_CLASSES)) + .isEqualTo("com.example.shaded.acme.foo.FooStaticExtension,com.example.shaded.acme.bar.SomeStaticExtension") + } + private fun buildJarFoo( entry: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, ): Path = buildJar("foo.jar") { @@ -92,7 +123,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { } private fun commonAssertions() { - val properties = outputShadowedJar.use { it.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR) }.toProperties() + val properties = outputShadowedJar.extensionModuleProperties assertThat(properties.getProperty(KEY_MODULE_NAME)).isEqualTo(MERGED_MODULE_NAME) assertThat(properties.getProperty(KEY_MODULE_VERSION)).isEqualTo(MERGED_MODULE_VERSION) @@ -108,6 +139,10 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { const val STATIC_EXTENSION_CLASSES_FOO = "com.acme.foo.FooStaticExtension" const val STATIC_EXTENSION_CLASSES_BAR = "com.acme.bar.SomeStaticExtension" + val JarPath.extensionModuleProperties get() = use { + it.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR).toProperties() + } + @JvmStatic fun resourcePathProvider() = listOf( Arguments.of(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index 064b9b3dc..ea7ef9b6c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.inputStream import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry +import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import java.util.Properties import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement @@ -45,8 +46,13 @@ public open class GroovyExtensionModuleTransformer : ResourceTransformer { } KEY_EXTENSION_CLASSES, KEY_STATIC_EXTENSION_CLASSES, - -> handle(key, value as String) { existingValue -> - module.setProperty(key, "$existingValue,$value") + -> { + val relocatedClasses = (value as String).split(',').joinToString(",") { className -> + context.relocators.relocateClass(className) + } + handle(key, relocatedClasses) { existingValue -> + module.setProperty(key, "$existingValue,$relocatedClasses") + } } } } From cc622ac86a5caa93a54b1c9a92aa7911c618351d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Sep 2025 23:41:25 +0800 Subject: [PATCH 728/941] Update dependency gradle to v9.1.0-rc-2 (#1711) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f5651b719..89bb9c649 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-rc-1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-rc-2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 33ebf044bb2dc6e42b1a3d7ae3fdc236bf0c1b80 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 08:31:56 +0800 Subject: [PATCH 729/941] Update plugin android-lint to v8.13.0 (#1712) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7c6f1ee05..d1d7adaa0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -34,7 +34,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.12.2" +android-lint = "com.android.lint:8.13.0" jetbrains-dokka = "org.jetbrains.dokka:2.0.0" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From d1d028b949253d8a06bac345b8ff6ef14702719a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Sep 2025 21:09:52 +0800 Subject: [PATCH 730/941] Update pluginPublish to v2 (major) (#1713) * Update pluginPublish to v2 * Remove `--no-configuration-cache` flag https://plugins.gradle.org/plugin/com.gradle.plugin-publish/2.0.0 --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- .github/workflows/release.yml | 2 +- gradle/libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8bd452eaf..7e4ef9dd4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -23,7 +23,7 @@ jobs: with: cache-read-only: true # TODO: https://github.com/gradle/gradle/issues/22779 - - run: ./gradlew publishToMavenCentral publishPlugins --no-configuration-cache + - run: ./gradlew publishToMavenCentral publishPlugins env: GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_KEY }} GRADLE_PUBLISH_SECRET: ${{ secrets.GRADLE_SECRET }} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d1d7adaa0..7ac37c936 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ jdkRelease = "11" kotlin = "2.2.10" moshi = "1.15.2" -pluginPublish = "1.3.1" +pluginPublish = "2.0.0" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" From 18d71d3d45f8cd62566319a5c9560b09845e1930 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 4 Sep 2025 17:00:37 +0800 Subject: [PATCH 731/941] Revert "Add guidelines for reviewing pull requests" This reverts commit 619a512749a6fd7027d92aab672302c1082bfb1c. --- .github/copilot-instructions.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index a2af306c5..000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,5 +0,0 @@ -Please review pull requests with the following criteria: -- **Documentation**: Carefully check the grammar, spelling, and clarity in Markdown or documentation files. Suggest improvements or rewrites for awkward phrasing. -- **Code**: Analyze all code changes for bugs, maintainability, and adherence to project conventions. Highlight any potential issues or improvements. -- **Coverage**: Ensure that new features or changes include appropriate tests and documentation. Request missing items. -- **Feedback**: Phrase suggestions and feedback in a constructive, respectful, and actionable way. From 0fc59cccb9a99df4dc737937beb7822daec61346 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 4 Sep 2025 17:36:42 +0800 Subject: [PATCH 732/941] Support relocating list of types in RelocatorRemapper (#1714) * Test `relocateMultiClassSignatureStringConstants` * Update `RelocatorRemapper` * Comment `classPattern` * Add unit test for `RelocatorRemapperTest` * Update changelog * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Cleanups * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 1 + .../gradle/plugins/shadow/RelocationTest.kt | 39 ++++++++++++++++++ .../shadow/internal/RelocatorRemapper.kt | 11 ++++- .../relocation/RelocatorRemapperTest.kt | 41 +++++++++++++++++++ 4 files changed, 90 insertions(+), 2 deletions(-) create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt diff --git a/docs/changes/README.md b/docs/changes/README.md index 2a417fbae..692d3477c 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,6 +7,7 @@ - Support relocating Groovy extensions in Module descriptors. ([#1705](https://github.com/GradleUp/shadow/pull/1705)) - Add extensions for `Iterable`. ([#1710](https://github.com/GradleUp/shadow/pull/1710)) +- Support relocating list of types in `RelocatorRemapper`. ([#1714](https://github.com/GradleUp/shadow/pull/1714)) ### Changed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index fc466d1a8..b4a56bc68 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -533,6 +533,45 @@ class RelocationTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/1403", + ) + @Test + fun relocateMultiClassSignatureStringConstants() { + writeClass { + """ + package my; + public class Main { + public static void main(String[] args) { + System.out.println("Lorg/package/ClassA;Lorg/package/ClassB;"); + System.out.println("(Lorg/package/ClassC;Lorg/package/ClassD;)"); + System.out.println("()Lorg/package/ClassE;Lorg/package/ClassF;"); + } + } + """.trimIndent() + } + projectScript.appendText( + """ + $shadowJarTask { + manifest { + attributes '$mainClassAttributeKey': 'my.Main' + } + relocate('org.package', 'shadow.org.package') + } + """.trimIndent(), + ) + + run(shadowJarPath) + val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) + + // Just check that the jar can be executed without NoClassDefFoundError. + assertThat(result).contains( + "Lshadow/org/package/ClassA;Lshadow/org/package/ClassB", + "Lshadow/org/package/ClassC;Lshadow/org/package/ClassD", + "Lshadow/org/package/ClassE;Lshadow/org/package/ClassF", + ) + } + @Test fun classBytesUnchangedIfPossible() { val mainClassEntry = writeClass() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 61f824852..922cb4709 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -16,7 +16,6 @@ internal class RelocatorRemapper( private val relocators: Set, private val onModified: () -> Unit = {}, ) : Remapper() { - private val classPattern: Pattern = Pattern.compile("(\\[*)?L(.+)") override fun mapValue(value: Any): Any { return if (value is String) { @@ -29,7 +28,11 @@ internal class RelocatorRemapper( override fun map(internalName: String): String = mapName(internalName) private fun mapName(name: String, mapLiterals: Boolean = false): String { - val newName = mapNameImpl(name, mapLiterals) + // Maybe a list of types. + val newName = name.split(';').joinToString(";") { + mapNameImpl(it, mapLiterals) + } + if (newName != name) { onModified() } @@ -60,4 +63,8 @@ internal class RelocatorRemapper( return name } + + private companion object { + val classPattern: Pattern = Pattern.compile("([\\[()]*)?L([^;]+);?") + } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt new file mode 100644 index 000000000..7bd10f1c6 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt @@ -0,0 +1,41 @@ +package com.github.jengelman.gradle.plugins.shadow.relocation + +import assertk.assertThat +import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.MethodSource + +class RelocatorRemapperTest { + @ParameterizedTest + @MethodSource("classSignatureStringConstants") + fun relocateSignaturePatterns(input: String, expected: String) { + val relocator = RelocatorRemapper( + relocators = setOf( + SimpleRelocator("org.package", "shadow.org.package"), + ), + ) + assertThat(relocator.map(input)).isEqualTo(expected) + } + + private companion object { + @JvmStatic + fun classSignatureStringConstants() = listOf( + // Normal class. + Arguments.of("Lorg/package/ClassA;", "Lshadow/org/package/ClassA;"), + // Array class. + Arguments.of("[Lorg/package/ClassA;", "[Lshadow/org/package/ClassA;"), + // Multidimensional array of class. + Arguments.of("[[Lorg/package/ClassA;", "[[Lshadow/org/package/ClassA;"), + // Multiple classes. + Arguments.of("Lorg/package/ClassA;Lorg/package/ClassB;", "Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;"), + // Multiple classes. + Arguments.of("Ljava/lang/Object;Lorg/package/ClassB;", "Ljava/lang/Object;Lshadow/org/package/ClassB;"), + // Method arguments. + Arguments.of("(Lorg/package/ClassA;Lorg/package/ClassB;)", "(Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;)"), + // Method return types. + Arguments.of("()Lorg/package/ClassA;Lorg/package/ClassB;", "()Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;"), + ) + } +} From efa16276f3d610bfe6d514637f5b4316751e8a7f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 6 Sep 2025 17:48:13 +0800 Subject: [PATCH 733/941] Prefer sh over shell --- docs/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.md b/docs/README.md index fead7c937..0a8e4f010 100644 --- a/docs/README.md +++ b/docs/README.md @@ -49,7 +49,7 @@ It must be available on the target system. Executable JARs contain a JAR MANIFEST that specifies the application Main Class. This allows the application to be started with a single command: -```shell +```sh java -jar application-shadow.jar ``` From 99da366017cab622d8b97bfc5acf387865a3dd0c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 7 Sep 2025 16:09:15 +0800 Subject: [PATCH 734/941] Note the breaking change caused by afterEvaluate usages (#1719) --- docs/changes/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index 692d3477c..0b8a9ff86 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -52,6 +52,7 @@ - Don't inject `TargetJvmVersion` attribute when automatic JVM targeting is disabled. ([#1666](https://github.com/GradleUp/shadow/pull/1666)) - Do not write modified class files for no-op relocations. ([#1694](https://github.com/GradleUp/shadow/pull/1694)) +- **BREAKING CHANGE:** The introduction of some `afterEvaluate` usages may cause configuration issues in rare cases. ## [9.0.2](https://github.com/GradleUp/shadow/releases/tag/9.0.2) - 2025-08-15 From 8b6090e2589f3d7349dbb6b9e96adb9121bbdea9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 8 Sep 2025 15:03:10 +0800 Subject: [PATCH 735/941] Fix excluding dependencies whose versions contain + (#1597) * Test `filterProjectThatVersionContainsPlus` * Update checks in `Dependency.toSpec` * Update changelog --- docs/changes/README.md | 4 +++ .../gradle/plugins/shadow/FilteringTest.kt | 27 +++++++++++++++++++ .../plugins/shadow/tasks/DependencyFilter.kt | 16 ++++++++--- 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 0b8a9ff86..5cc15ea96 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -15,6 +15,10 @@ The Gradle Module descriptors (`org.codehaus.groovy.runtime.ExtensionModule` files) defined under `META-INF/services/` and `META-INF/groovy` will be merged into `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`. +### Fixed + +- Fix excluding dependencies whose versions contain `+`. ([#1597](https://github.com/GradleUp/shadow/pull/1597)) + ## [9.1.0](https://github.com/GradleUp/shadow/compare/9.1.0) - 2025-08-29 ### Added diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index be0b10090..062259a00 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText @@ -174,6 +175,32 @@ class FilteringTest : BasePluginTest() { } } + @Issue( + "https://github.com/GradleUp/shadow/issues/671", + ) + @Test + fun filterProjectThatVersionContainsPlus() { + writeClientAndServerModules( + serverShadowBlock = """ + dependencies { + exclude(project(':client')) + } + """.trimIndent(), + ) + path("client/build.gradle").appendText("version = '1.0.0+1'") + + run(serverShadowJarPath) + + assertThat(outputServerShadowedJar).useAll { + containsOnly( + "server/", + "server/Server.class", + *junitEntries, + *manifestEntries, + ) + } + } + @Test fun excludeTransitiveProjectDependency() { writeClientAndServerModules( diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index 43f4677c6..8a0d7a683 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -109,9 +109,19 @@ public interface DependencyFilter : Serializable { private fun Dependency.toSpec(): Spec { return Spec { resolvedDependency -> - (group == null || resolvedDependency.moduleGroup.matches(group!!.toRegex())) && - resolvedDependency.moduleName.matches(name.toRegex()) && - (version == null || resolvedDependency.moduleVersion.matches(version!!.toRegex())) + val groupMatch = group?.let { + it == resolvedDependency.moduleGroup || resolvedDependency.moduleGroup.matches(it.toRegex()) + } ?: true + val nameMatch = name.let { + it == resolvedDependency.moduleName || resolvedDependency.moduleName.matches(it.toRegex()) + } + val versionMatch = version?.let { + // Version like `1.0.0+1` can't be converted to regex directly because `+` is a special character in regex. + // So we check for exact match first, then fallback to regex match. + it == resolvedDependency.moduleVersion || resolvedDependency.moduleVersion.matches(it.toRegex()) + } ?: true + + groupMatch && nameMatch && versionMatch } } } From 187cf157a27b6405d808ad84051948fd30cee2c3 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 8 Sep 2025 18:30:32 +0800 Subject: [PATCH 736/941] Move injecting Class-Path manifest attr logic from doFirst into copy (#1720) * Replace `doFirst` with `parentJarTask` * Try out `parentManifest` * Add `SerializableManifest` * Remove non-serializable types * Fix CC * Fix `addShadowConfigurationToClassPathInManifest` * Update lint baseline * Clean up `DefaultInheritManifest` * Update changelog * Update docs/changes/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 1 + gradle/lint-baseline.xml | 75 +++++++++++-------- .../shadow/internal/DefaultInheritManifest.kt | 12 ++- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 29 +++---- 4 files changed, 69 insertions(+), 48 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 5cc15ea96..3e414b686 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -14,6 +14,7 @@ - Merge Gradle Module descriptors into the modern `META-INF` path. ([#1706](https://github.com/GradleUp/shadow/pull/1706)) The Gradle Module descriptors (`org.codehaus.groovy.runtime.ExtensionModule` files) defined under `META-INF/services/` and `META-INF/groovy` will be merged into `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`. +- Move injecting `Class-Path` manifest attr logic from `doFirst` into `copy`. ([#1720](https://github.com/GradleUp/shadow/pull/1720)) ### Fixed diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index 1789842c8..7db2e3ecd 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -1,5 +1,5 @@ - + + + + + @@ -19,7 +30,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -30,10 +41,32 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> + + + + + + + + @@ -96,7 +129,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -107,7 +140,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +151,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -129,7 +162,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -140,7 +173,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -166,26 +199,4 @@ column="1"/> - - - - - - - - diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt index 54bad071f..4fa4a5658 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt @@ -2,15 +2,21 @@ package com.github.jengelman.gradle.plugins.shadow.internal import com.github.jengelman.gradle.plugins.shadow.tasks.InheritManifest import org.gradle.api.Action +import org.gradle.api.Project import org.gradle.api.internal.file.FileResolver +import org.gradle.api.internal.project.DefaultProject import org.gradle.api.java.archives.Manifest import org.gradle.api.java.archives.ManifestMergeSpec import org.gradle.api.java.archives.internal.DefaultManifest import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec -internal class DefaultInheritManifest @JvmOverloads constructor( - private val fileResolver: FileResolver, - private val internalManifest: DefaultManifest = DefaultManifest(fileResolver), +internal class DefaultInheritManifest( + project: Project, + manifest: Manifest? = null, + // `AbstractTask.getServices` is protected, we need to get it via `DefaultProject`. + // https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/java/org/gradle/api/internal/AbstractTask.java#L194 + private val fileResolver: FileResolver = (project as DefaultProject).services.get(FileResolver::class.java), + private val internalManifest: Manifest = manifest ?: DefaultManifest(fileResolver), ) : InheritManifest, Manifest by internalManifest { private val inheritMergeSpecs = mutableListOf() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index cbe130cc9..9be75117d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -64,6 +64,7 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin @CacheableTask public abstract class ShadowJar : Jar() { private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) + private val shadowDependencies = project.provider { project.files(project.configurations.shadow) } init { group = LifecycleBasePlugin.BUILD_GROUP @@ -71,7 +72,7 @@ public abstract class ShadowJar : Jar() { // https://github.com/gradle/gradle/blob/df5bc230c57db70aa3f6909403e5f89d7efde531/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/copy/DuplicateHandlingCopyActionDecorator.java#L55-L64 duplicatesStrategy = EXCLUDE - manifest = DefaultInheritManifest(services.get(FileResolver::class.java)) + manifest = DefaultInheritManifest(project) outputs.doNotCacheIf("Has one or more transforms or relocators that are not cacheable") { transformers.get().any { !it::class.hasAnnotation() } || @@ -369,7 +370,7 @@ public abstract class ShadowJar : Jar() { } } } - injectMultiReleaseAttrIfPresent() + injectManifestAttributes() super.copy() } @@ -450,7 +451,14 @@ public abstract class ShadowJar : Jar() { } } - private fun injectMultiReleaseAttrIfPresent() { + private fun injectManifestAttributes() { + val classPathAttr = manifest.attributes[classPathAttributeKey]?.toString().orEmpty() + val shadowFiles = shadowDependencies.get() + if (!shadowFiles.isEmpty) { + val attrs = listOf(classPathAttr) + shadowFiles.map { it.name } + manifest.attributes[classPathAttributeKey] = attrs.joinToString(" ").trim() + } + if (addMultiReleaseAttribute.get()) { logger.info("Adding $multiReleaseAttributeKey attribute to the manifest if any dependencies contain it.") } else { @@ -496,16 +504,11 @@ public abstract class ShadowJar : Jar() { "module-info.class", ) - @Suppress("EagerGradleConfiguration") // mergeSpec.from hasn't supported lazy configuration yet. - task.manifest.inheritFrom(jarTask.get().manifest) - val classPathAttr = jarTask.map { it.manifest.attributes[classPathAttributeKey]?.toString().orEmpty() } - val shadowFiles = files(configurations.shadow) - task.doFirst("Set $classPathAttributeKey attribute in the manifest") { - if (!shadowFiles.isEmpty) { - val attrs = listOf(classPathAttr.get()) + shadowFiles.map { it.name } - task.manifest.attributes[classPathAttributeKey] = attrs.joinToString(" ").trim() - } - } + task.manifest = DefaultInheritManifest( + project, + @Suppress("EagerGradleConfiguration") // The ctor doesn't support Provider. + jarTask.get().manifest, + ) @Suppress("EagerGradleConfiguration") // Can't use `named` as the task is optional. tasks.findByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME)?.dependsOn(task) From 2afeeceddfffa91c228e17d0877e480537890690 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 8 Sep 2025 10:47:46 +0000 Subject: [PATCH 737/941] Update dependency gradle to v9.1.0-rc-3 (#1721) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 89bb9c649..843452989 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-rc-2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-rc-3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From f7d8d1b3ace73db99d428f3d47b1a11ebdfec711 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 8 Sep 2025 19:12:47 +0800 Subject: [PATCH 738/941] Deprecate InheritManifest (#1722) * Try to remove `InheritManifest` * Revert "Try to remove `InheritManifest`" This reverts commit 1a0745d4953635e1e1d69bc38feb213e3d18a89b. * Mark `Deprecated` * Update changelog --- docs/changes/README.md | 1 + .../gradle/plugins/shadow/internal/DefaultInheritManifest.kt | 2 ++ .../jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt | 4 ++++ .../github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 1 + 4 files changed, 8 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index 3e414b686..8c32da476 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -15,6 +15,7 @@ The Gradle Module descriptors (`org.codehaus.groovy.runtime.ExtensionModule` files) defined under `META-INF/services/` and `META-INF/groovy` will be merged into `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`. - Move injecting `Class-Path` manifest attr logic from `doFirst` into `copy`. ([#1720](https://github.com/GradleUp/shadow/pull/1720)) +- Deprecate `InheritManifest`. ([#1722](https://github.com/GradleUp/shadow/pull/1722)) ### Fixed diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt index 4fa4a5658..f807aa8a6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt @@ -1,3 +1,5 @@ +@file:Suppress("DEPRECATION") + package com.github.jengelman.gradle.plugins.shadow.internal import com.github.jengelman.gradle.plugins.shadow.tasks.InheritManifest diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt index 8fe11619d..872a51415 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt @@ -4,6 +4,10 @@ import org.gradle.api.Action import org.gradle.api.java.archives.Manifest import org.gradle.api.java.archives.ManifestMergeSpec +@Deprecated( + message = "This is deprecated and will be removed in a future release. `inheritFrom` should be replaced by `from`.", + replaceWith = ReplaceWith("Manifest", "org.gradle.api.java.archives.Manifest"), +) public interface InheritManifest : Manifest { public fun inheritFrom(vararg inheritPaths: Any) { inheritFrom(inheritPaths = inheritPaths, action = {}) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 9be75117d..6471fd503 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -195,6 +195,7 @@ public abstract class ShadowJar : Jar() { @get:Option(option = "add-multi-release-attribute", description = "Adds the multi-release attribute to the manifest if any dependencies contain it.") public open val addMultiReleaseAttribute: Property = objectFactory.property(true) + @Suppress("DEPRECATION") // TODO: replace the usage of deprecated InheritManifest. @Internal override fun getManifest(): InheritManifest = super.getManifest() as InheritManifest From fa1fe3152aada653edfe4f351717835d83fd2335 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 8 Sep 2025 19:30:57 +0800 Subject: [PATCH 739/941] Prefer using from over inheritFrom (#1723) --- docs/configuration/README.md | 6 +++--- .../jengelman/gradle/plugins/shadow/JavaPluginsTest.kt | 2 +- .../gradle/plugins/shadow/tasks/InheritManifest.kt | 6 +++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 7a38b0051..91ceaed8d 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -122,7 +122,7 @@ Inspecting the `META-INF/MANIFEST.MF` entry in the JAR file will reveal the foll Class-Path: /libs/foo.jar ``` -If it is desired to inherit a manifest from a JAR task other than the standard [`Jar`][Jar] task, the `inheritFrom` +If it is desired to inherit a manifest from a JAR task other than the standard [`Jar`][Jar] task, the `from` methods on the `shadowJar.manifest` object can be used to configure the upstream. === "Kotlin" @@ -135,7 +135,7 @@ methods on the `shadowJar.manifest` object can be used to configure the upstream } tasks.shadowJar { - manifest.inheritFrom(testJar.get().manifest) + manifest.from(testJar.get().manifest) } ``` @@ -149,7 +149,7 @@ methods on the `shadowJar.manifest` object can be used to configure the upstream } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - manifest.inheritFrom(testJar.get().manifest) + manifest.from(testJar.get().manifest) } ``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 4d01bfac2..f4e8637f7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -724,7 +724,7 @@ class JavaPluginsTest : BasePluginTest() { } } $shadowJarTask { - manifest.inheritFrom(testJar.get().manifest) + manifest.from(testJar.get().manifest) } """.trimIndent(), ) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt index 872a51415..fef8d50ff 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest.kt @@ -5,10 +5,14 @@ import org.gradle.api.java.archives.Manifest import org.gradle.api.java.archives.ManifestMergeSpec @Deprecated( - message = "This is deprecated and will be removed in a future release. `inheritFrom` should be replaced by `from`.", + message = "This is deprecated and will be removed in a future release.", replaceWith = ReplaceWith("Manifest", "org.gradle.api.java.archives.Manifest"), ) public interface InheritManifest : Manifest { + @Deprecated( + message = "This is deprecated and will be removed in a future release.", + replaceWith = ReplaceWith("from"), + ) public fun inheritFrom(vararg inheritPaths: Any) { inheritFrom(inheritPaths = inheritPaths, action = {}) } From 710db9e5bb82c4358a8555738421ca3cc6656dd4 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 8 Sep 2025 19:28:49 +0800 Subject: [PATCH 740/941] Remove unused FileResolver import --- gradle/lint-baseline.xml | 27 ++++++------------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 1 - 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index 7db2e3ecd..db5934ea8 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -8,7 +8,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -19,7 +19,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -30,7 +30,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -41,7 +41,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -52,7 +52,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -63,7 +63,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -74,7 +74,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -177,17 +177,6 @@ column="9"/> - - - - diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 6471fd503..7d174cbc0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -40,7 +40,6 @@ import org.gradle.api.file.DuplicatesStrategy.FAIL import org.gradle.api.file.DuplicatesStrategy.INCLUDE import org.gradle.api.file.DuplicatesStrategy.INHERIT import org.gradle.api.file.DuplicatesStrategy.WARN -import org.gradle.api.internal.file.FileResolver import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty From 8ff959edf180451ad441b87919077cc4207f5d15 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 8 Sep 2025 22:24:21 +0800 Subject: [PATCH 741/941] Use default JavaExec error message when main class is not set (#1725) --- docs/changes/README.md | 1 + .../gradle/plugins/shadow/ApplicationPluginTest.kt | 3 ++- .../gradle/plugins/shadow/ShadowApplicationPlugin.kt | 7 ++----- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 8c32da476..90519ea55 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -16,6 +16,7 @@ and `META-INF/groovy` will be merged into `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`. - Move injecting `Class-Path` manifest attr logic from `doFirst` into `copy`. ([#1720](https://github.com/GradleUp/shadow/pull/1720)) - Deprecate `InheritManifest`. ([#1722](https://github.com/GradleUp/shadow/pull/1722)) +- Use default `JavaExec` error message when main class is not set. ([#1725](https://github.com/GradleUp/shadow/pull/1725)) ### Fixed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 386311bf0..8321254b0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -177,7 +177,8 @@ class ApplicationPluginTest : BasePluginTest() { val result = runWithFailure(runShadowPath) assertThat(result.output).contains( - "The main class must be specified and not left empty in `application.mainClass` or manifest attributes.", + "Error: Could not find or load main class", + "Caused by: java.lang.ClassNotFoundException:", ) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 91b7facf1..8505c727e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -132,12 +132,9 @@ public abstract class ShadowApplicationPlugin : Plugin { tasks.shadowJar.configure { task -> task.inputs.property("mainClassName", mainClassName) task.doFirst("Set $mainClassAttributeKey attribute in the manifest") { + val realClass = mainClassName.orNull // Inject the attribute if it is not already present. - if (!task.manifest.attributes.contains(mainClassAttributeKey)) { - val realClass = mainClassName.orNull - if (realClass.isNullOrEmpty()) { - error("The main class must be specified and not left empty in `application.mainClass` or manifest attributes.") - } + if (!task.manifest.attributes.contains(mainClassAttributeKey) && !realClass.isNullOrEmpty()) { task.manifest.attributes[mainClassAttributeKey] = realClass } } From 75f67367bc19f0b21b8f439da3cbbcbb478c1fee Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 9 Sep 2025 10:34:15 +0800 Subject: [PATCH 742/941] Move injecting Main-Class manifest attr logic from doFirst into copy (#1724) * Replace `doFirst` with `mainClass` * Update changelog * Fix `errorWhenMainClassNotSet` * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update docs * Add tests --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 1 + docs/changes/README.md | 8 ++ docs/getting-started/README.md | 1 + gradle/lint-baseline.xml | 10 +-- .../plugins/shadow/ApplicationPluginTest.kt | 21 +++++- .../gradle/plugins/shadow/JavaPluginsTest.kt | 75 ++++++++++++++++++- .../plugins/shadow/ShadowApplicationPlugin.kt | 12 +-- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 11 +-- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 28 +++++++ 9 files changed, 138 insertions(+), 29 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index c983f9eaf..3ce4bddbf 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -204,6 +204,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getFailOnDuplicateEntries ()Lorg/gradle/api/provider/Property; public fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; public fun getIncludes ()Ljava/util/Set; + public fun getMainClass ()Lorg/gradle/api/provider/Property; public fun getManifest ()Lcom/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest; public synthetic fun getManifest ()Lorg/gradle/api/java/archives/Manifest; public fun getMinimizeJar ()Lorg/gradle/api/provider/Property; diff --git a/docs/changes/README.md b/docs/changes/README.md index 90519ea55..5fa5eecef 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -8,6 +8,13 @@ - Support relocating Groovy extensions in Module descriptors. ([#1705](https://github.com/GradleUp/shadow/pull/1705)) - Add extensions for `Iterable`. ([#1710](https://github.com/GradleUp/shadow/pull/1710)) - Support relocating list of types in `RelocatorRemapper`. ([#1714](https://github.com/GradleUp/shadow/pull/1714)) +- Add `mainClass` property into `ShadowJar`. ([#1722](https://github.com/GradleUp/shadow/pull/1722)) + ```kotlin + tasks.shadowJar { + // This property will be used as a fallback if there is no explicit `Main-Class` attribute set. + mainClass = "my.Main" + } + ``` ### Changed @@ -15,6 +22,7 @@ The Gradle Module descriptors (`org.codehaus.groovy.runtime.ExtensionModule` files) defined under `META-INF/services/` and `META-INF/groovy` will be merged into `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`. - Move injecting `Class-Path` manifest attr logic from `doFirst` into `copy`. ([#1720](https://github.com/GradleUp/shadow/pull/1720)) +- Move injecting `Main-Class` manifest attr logic from `doFirst` into `copy`. ([#1724](https://github.com/GradleUp/shadow/pull/1724)) - Deprecate `InheritManifest`. ([#1722](https://github.com/GradleUp/shadow/pull/1722)) - Use default `JavaExec` error message when main class is not set. ([#1725](https://github.com/GradleUp/shadow/pull/1725)) diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 9d8101bcd..287c32528 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -154,6 +154,7 @@ Here are the options that can be passed to the `shadowJar`: --no-enable-auto-relocation Disables option --enable-auto-relocation. --fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate. --no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries. +--main-class Main class attribute to add to manifest. --minimize-jar Minimizes the jar by removing unused classes. --no-minimize-jar Disables option --minimize-jar. --relocation-prefix Prefix used for auto relocation of packages in the dependencies. diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index db5934ea8..d22512c46 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -85,7 +85,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -96,7 +96,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -107,7 +107,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -118,7 +118,7 @@ errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> @@ -184,7 +184,7 @@ errorLine2="~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"> diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 8321254b0..964fb5349 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -170,6 +170,24 @@ class ApplicationPluginTest : BasePluginTest() { assertions(result.output, "bar") } + @Test + fun overrideMainClassFromApplicationPlugin() { + prepare() + projectScript.appendText( + """ + $shadowJarTask { + mainClass = 'my.Main2' // Different from application.mainClass. + } + """.trimIndent(), + ) + + run(runShadowPath) // Run without errors. + + assertThat(jarPath("build/libs/myapp-1.0-all.jar")).useAll { + getMainAttr(mainClassAttributeKey).isEqualTo("my.Main2") + } + } + @Test fun errorWhenMainClassNotSet() { prepare(mainClassBlock = "") @@ -177,8 +195,7 @@ class ApplicationPluginTest : BasePluginTest() { val result = runWithFailure(runShadowPath) assertThat(result.output).contains( - "Error: Could not find or load main class", - "Caused by: java.lang.ClassNotFoundException:", + "no main manifest attribute, in", ) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index f4e8637f7..400d843da 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -48,7 +48,9 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.testfixtures.ProjectBuilder import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.EnumSource +import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.ValueSource class JavaPluginsTest : BasePluginTest() { @@ -104,6 +106,7 @@ class JavaPluginsTest : BasePluginTest() { assertThat(enableAutoRelocation.get()).isFalse() assertThat(failOnDuplicateEntries.get()).isFalse() assertThat(minimizeJar.get()).isFalse() + assertThat(mainClass.orNull).isNull() assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) assertThat(configurations.get()).all { @@ -126,6 +129,7 @@ class JavaPluginsTest : BasePluginTest() { "--no-enable-auto-relocation Disables option --enable-auto-relocation.", "--fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate.", "--no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries", + "--main-class Main class attribute to add to manifest.", "--minimize-jar Minimizes the jar by removing unused classes.", "--no-minimize-jar Disables option --minimize-jar.", "--relocation-prefix Prefix used for auto relocation of packages in the dependencies.", @@ -710,7 +714,7 @@ class JavaPluginsTest : BasePluginTest() { } @Test - fun inheritFromOtherManifest() { + fun inheritManifestAttrsFromJars() { projectScript.appendText( """ $jarTask { @@ -738,6 +742,32 @@ class JavaPluginsTest : BasePluginTest() { } } + @Test + fun inheritManifestMainClassFromJar() { + projectScript.appendText( + """ + $jarTask { + manifest { + attributes '$mainClassAttributeKey': 'my.Main' + } + } + $shadowJarTask { + mainClass = 'my.Main2' // This should not override the inherited one. + } + """.trimIndent(), + ) + + val result = run(shadowJarPath, infoArgument) + + assertThat(result.output).contains( + "Skipping adding $mainClassAttributeKey attribute to the manifest as it is already set.", + ) + assertThat(outputShadowedJar).useAll { + transform { it.mainAttrSize }.isGreaterThan(1) + getMainAttr("Main-Class").isEqualTo("my.Main") + } + } + @Test fun addExtraFilesViaFrom() { val mainClassEntry = writeClass() @@ -950,7 +980,50 @@ class JavaPluginsTest : BasePluginTest() { ) } + @ParameterizedTest + @MethodSource("fallbackMainClassProvider") + fun fallbackMainClassByProperty(input: String, expected: String?, message: String) { + projectScript.appendText( + """ + $shadowJarTask { + mainClass = '$input' + } + """.trimIndent(), + ) + + val result = run(shadowJarPath, infoArgument) + + assertThat(result.output).contains( + message, + ) + assertThat(outputShadowedJar).useAll { + getMainAttr(mainClassAttributeKey).isEqualTo(expected) + } + } + + @ParameterizedTest + @MethodSource("fallbackMainClassProvider") + fun fallbackMainClassByCliOption(input: String, expected: String?) { + if (input.isEmpty()) { + run(shadowJarPath) + } else { + run(shadowJarPath, "--main-class", input) + } + + assertThat(outputShadowedJar).useAll { + getMainAttr(mainClassAttributeKey).isEqualTo(expected) + } + } + private fun dependencies(configuration: String, vararg flags: String): String { return run("dependencies", "--configuration", configuration, *flags).output } + + private companion object { + @JvmStatic + fun fallbackMainClassProvider() = listOf( + Arguments.of("my.Main", "my.Main", "Adding $mainClassAttributeKey attribute to the manifest with value"), + Arguments.of("", null, "Skipping adding $mainClassAttributeKey attribute to the manifest as it is empty."), + ) + } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 8505c727e..04834b9e6 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -6,7 +6,6 @@ import com.github.jengelman.gradle.plugins.shadow.internal.applicationExtension import com.github.jengelman.gradle.plugins.shadow.internal.distributions import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension import com.github.jengelman.gradle.plugins.shadow.internal.javaToolchainService -import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shadowJar import org.gradle.api.GradleException @@ -127,17 +126,8 @@ public abstract class ShadowApplicationPlugin : Plugin { } protected open fun Project.configureShadowJarMainClass() { - // Default to empty string to avoid the error of the value not being configured yet. - val mainClassName = applicationExtension.mainClass.convention("") tasks.shadowJar.configure { task -> - task.inputs.property("mainClassName", mainClassName) - task.doFirst("Set $mainClassAttributeKey attribute in the manifest") { - val realClass = mainClassName.orNull - // Inject the attribute if it is not already present. - if (!task.manifest.attributes.contains(mainClassAttributeKey) && !realClass.isNullOrEmpty()) { - task.manifest.attributes[mainClassAttributeKey] = realClass - } - } + task.mainClass.convention(applicationExtension.mainClass) } } 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 5c1456cdc..6f2e16030 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,7 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.internal.isAtLeastKgpVersion -import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.registerShadowJarCommon import org.gradle.api.Plugin @@ -41,15 +40,7 @@ public abstract class ShadowKmpPlugin : Plugin { @OptIn(ExperimentalKotlinGradlePluginApi::class) target.mainRun { - // 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("Set $mainClassAttributeKey attribute in the manifest") { - val realClass = mainClassName.get().orNull - if (!task.manifest.attributes.contains(mainClassAttributeKey) && !realClass.isNullOrEmpty()) { - task.manifest.attributes[mainClassAttributeKey] = realClass - } - } + task.mainClass.convention(mainClass) } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 7d174cbc0..449b88c69 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -8,6 +8,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFil import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.fileCollection +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.setProperty @@ -169,6 +170,19 @@ public abstract class ShadowJar : Jar() { @get:Option(option = "relocation-prefix", description = "Prefix used for auto relocation of packages in the dependencies.") public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) + /** + * Main class attribute to add to manifest. + * + * This property will be used as a fallback if there is no explicit `Main-Class` attribute set for the [ShadowJar] + * task or the main [Jar] task. + * + * Defaults to `null`. + */ + @get:Optional + @get:Input + @get:Option(option = "main-class", description = "Main class attribute to add to manifest.") + public open val mainClass: Property = objectFactory.property() + /** * Fails build if the ZIP entries in the shadowed JAR are duplicate. * @@ -452,6 +466,20 @@ public abstract class ShadowJar : Jar() { } private fun injectManifestAttributes() { + val mainClassValue = mainClass.orNull + when { + manifest.attributes.contains(mainClassAttributeKey) -> { + logger.info("Skipping adding $mainClassAttributeKey attribute to the manifest as it is already set.") + } + mainClassValue.isNullOrEmpty() -> { + logger.info("Skipping adding $mainClassAttributeKey attribute to the manifest as it is empty.") + } + else -> { + manifest.attributes[mainClassAttributeKey] = mainClassValue + logger.info("Adding $mainClassAttributeKey attribute to the manifest with value '$mainClassValue'.") + } + } + val classPathAttr = manifest.attributes[classPathAttributeKey]?.toString().orEmpty() val shadowFiles = shadowDependencies.get() if (!shadowFiles.isEmpty) { From 975e8a82a290980cda290bdc0610e9116e61fac9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 10 Sep 2025 15:26:32 +0800 Subject: [PATCH 743/941] Rename test lifecycle functions (#1727) --- .../jengelman/gradle/plugins/shadow/BasePluginTest.kt | 8 ++++---- .../jengelman/gradle/plugins/shadow/FilteringTest.kt | 4 ++-- .../jengelman/gradle/plugins/shadow/GroovyPluginTest.kt | 4 ++-- .../jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt | 4 ++-- .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 4 ++-- .../jengelman/gradle/plugins/shadow/ScalaPluginTest.kt | 4 ++-- .../plugins/shadow/transformers/BaseTransformerTest.kt | 4 ++-- .../plugins/shadow/transformers/BaseTransformerTest.kt | 2 +- .../shadow/transformers/ServiceFileTransformerTest.kt | 6 +++--- 9 files changed, 20 insertions(+), 20 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 b39baf428..549f34ee9 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 @@ -63,7 +63,7 @@ abstract class BasePluginTest { val outputServerShadowedJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") @BeforeAll - open fun doFirst() { + fun beforeAll() { localRepo = AppendableMavenRepository( createTempDirectory().resolve("local-maven-repo").createDirectories(), runner(projectDir = null), @@ -124,18 +124,18 @@ abstract class BasePluginTest { } @BeforeEach - open fun setup() { + open fun beforeEach() { projectScript.writeText(getDefaultProjectBuildScript(withGroup = true, withVersion = true)) settingsScript.writeText(getDefaultSettingsBuildScript()) } @AfterEach - fun cleanup() { + fun afterEach() { println(projectScript.readText()) } @AfterAll - fun doLast() { + fun afterAll() { @OptIn(ExperimentalPathApi::class) localRepo.root.deleteRecursively() } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 062259a00..ff35a1e78 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -12,8 +12,8 @@ import org.junit.jupiter.params.provider.ValueSource class FilteringTest : BasePluginTest() { @BeforeEach - override fun setup() { - super.setup() + override fun beforeEach() { + super.beforeEach() projectScript.appendText( """ dependencies { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt index 7c85e8a07..bea1706d9 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt @@ -10,8 +10,8 @@ import org.junit.jupiter.api.Test class GroovyPluginTest : BasePluginTest() { @BeforeEach - override fun setup() { - super.setup() + override fun beforeEach() { + super.beforeEach() val projectBuildScript = getDefaultProjectBuildScript( plugin = "groovy", withGroup = true, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 7c875c1c9..ad41a3b9d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -19,8 +19,8 @@ import org.junit.jupiter.params.provider.ValueSource class KotlinPluginsTest : BasePluginTest() { @BeforeEach - override fun setup() { - super.setup() + override fun beforeEach() { + super.beforeEach() val projectBuildScript = getDefaultProjectBuildScript( plugin = "org.jetbrains.kotlin.multiplatform", withGroup = true, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index d7eac8c44..f3a6c138f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -54,8 +54,8 @@ class PublishingTest : BasePluginTest() { lateinit var remoteRepoPath: Path @BeforeEach - override fun setup() { - super.setup() + override fun beforeEach() { + super.beforeEach() settingsScript.appendText("rootProject.name = 'maven'$lineSeparator") } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt index e365bc6bd..e732701ae 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt @@ -10,8 +10,8 @@ import org.junit.jupiter.api.Test class ScalaPluginTest : BasePluginTest() { @BeforeEach - override fun setup() { - super.setup() + override fun beforeEach() { + super.beforeEach() val projectBuildScript = getDefaultProjectBuildScript( plugin = "scala", withGroup = true, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 69202dd34..75e6881b1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -8,8 +8,8 @@ import org.junit.jupiter.api.BeforeEach abstract class BaseTransformerTest : BasePluginTest() { @BeforeEach - override fun setup() { - super.setup() + override fun beforeEach() { + super.beforeEach() projectScript.appendText( """ $shadowJarTask { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 4cc33eb2c..77ec6bde6 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -23,7 +23,7 @@ abstract class BaseTransformerTest { get() = TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME)) @BeforeEach - open fun setup() { + open fun beforeEach() { @Suppress("UNCHECKED_CAST") val clazz = (this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments.first() as Class transformer = clazz.create(testObjectFactory) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 28f539c8d..7fb5c2616 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -27,13 +27,13 @@ class ServiceFileTransformerTest : BaseTransformerTest() private lateinit var tempJar: Path @BeforeEach - override fun setup() { - super.setup() + override fun beforeEach() { + super.beforeEach() tempJar = createTempFile("shade.", ".jar") } @AfterEach - fun cleanup() { + fun afterEach() { tempJar.deleteExisting() } From b873c701c1d96c14d5e6f57192c7de18888b9b48 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 08:38:21 +0000 Subject: [PATCH 744/941] Update kotlin monorepo to v2.2.20 (#1728) * Update kotlin monorepo to v2.2.20 * Replace plugins `withType` with `withId` Fixes ``` e: warnings found and -Werror specified w: file:///Users/goooler/IdeaProjects/shadow/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt:118:13 Type argument is not within its bounds: must be subtype of '(Plugin..Plugin<*>?)'. w: file:///Users/goooler/IdeaProjects/shadow/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt:17:5 Type argument is not within its bounds: must be subtype of '(Plugin..Plugin<*>?)'. w: file:///Users/goooler/IdeaProjects/shadow/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPlugin.kt:21:5 Type argument is not within its bounds: must be subtype of '(Plugin..Plugin<*>?)'. ``` --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- gradle/libs.versions.toml | 2 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 4 ++-- .../jengelman/gradle/plugins/shadow/ShadowPlugin.kt | 10 ++++------ 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7ac37c936..4519d1cbf 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] jdkRelease = "11" -kotlin = "2.2.10" +kotlin = "2.2.20" moshi = "1.15.2" pluginPublish = "2.0.0" 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 b098c9a19..1f30c01b3 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.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.tasks.bundling.Jar -import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin public abstract class ShadowJavaPlugin @Inject constructor( private val softwareComponentFactory: SoftwareComponentFactory, @@ -115,7 +114,8 @@ public abstract class ShadowJavaPlugin @Inject constructor( } protected open fun Project.configureJavaGradlePlugin() { - plugins.withType(JavaGradlePluginPlugin::class.java).configureEach { + // org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin + plugins.withId("org.gradle.java-gradle-plugin") { val gradleApi = dependencies.gradleApi() // Remove the gradleApi so it isn't merged into the jar file. // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. 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 7d176d2f1..de22b3038 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 @@ -6,19 +6,17 @@ import com.github.jengelman.gradle.plugins.shadow.internal.findOptionalProperty import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import org.gradle.api.Plugin import org.gradle.api.Project -import org.gradle.api.plugins.ApplicationPlugin -import org.gradle.api.plugins.JavaPlugin public abstract class ShadowPlugin : Plugin { override fun apply(project: Project): Unit = with(project.plugins) { apply(ShadowBasePlugin::class.java) - @Suppress("WithTypeWithoutConfigureEach") - withType(JavaPlugin::class.java) { + // org.gradle.api.plugins.JavaPlugin + withId("org.gradle.java") { apply(ShadowJavaPlugin::class.java) } - @Suppress("WithTypeWithoutConfigureEach") - withType(ApplicationPlugin::class.java) { + // org.gradle.api.plugins.ApplicationPlugin + withId("org.gradle.application") { apply(ShadowApplicationPlugin::class.java) } withId(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { From 5497d138557b6851be85fbd64fc3d1d768d04edc Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 11 Sep 2025 09:53:57 +0800 Subject: [PATCH 745/941] Update RelocatorRemapper class pattern to cover more Java method descriptors (#1731) * Test more cases like `void method(boolean arg1, org.package.ClassA arg2)` in `relocateSignaturePatterns` * Update `classPattern` * Update changelog --- docs/changes/README.md | 1 + .../gradle/plugins/shadow/internal/RelocatorRemapper.kt | 5 ++++- .../plugins/shadow/relocation/RelocatorRemapperTest.kt | 9 ++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 5fa5eecef..c210100a6 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -25,6 +25,7 @@ - Move injecting `Main-Class` manifest attr logic from `doFirst` into `copy`. ([#1724](https://github.com/GradleUp/shadow/pull/1724)) - Deprecate `InheritManifest`. ([#1722](https://github.com/GradleUp/shadow/pull/1722)) - Use default `JavaExec` error message when main class is not set. ([#1725](https://github.com/GradleUp/shadow/pull/1725)) +- Update `RelocatorRemapper` class pattern to cover more Java method descriptors. ([#1731](https://github.com/GradleUp/shadow/pull/1731)) ### Fixed diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 922cb4709..27645ce27 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -65,6 +65,9 @@ internal class RelocatorRemapper( } private companion object { - val classPattern: Pattern = Pattern.compile("([\\[()]*)?L([^;]+);?") + /** + * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html + */ + val classPattern: Pattern = Pattern.compile("([\\[()BCDFIJSZ]*)?L([^;]+);?") } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt index 7bd10f1c6..6a32c7c55 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt @@ -20,6 +20,13 @@ class RelocatorRemapperTest { } private companion object { + val primitiveTypes = setOf('B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z') + + val primitiveTypeArguments = primitiveTypes.map { + // Methods like `void method(boolean arg1, org.package.ClassA arg2)` + Arguments.of("(${it}Lorg/package/ClassA;)V", "(${it}Lshadow/org/package/ClassA;)V") + } + @JvmStatic fun classSignatureStringConstants() = listOf( // Normal class. @@ -36,6 +43,6 @@ class RelocatorRemapperTest { Arguments.of("(Lorg/package/ClassA;Lorg/package/ClassB;)", "(Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;)"), // Method return types. Arguments.of("()Lorg/package/ClassA;Lorg/package/ClassB;", "()Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;"), - ) + ) + primitiveTypeArguments } } From 60e3193db4933e041b2192f69eac6be5c444b09b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 12 Sep 2025 22:26:33 +0800 Subject: [PATCH 746/941] Test single method argument in relocateSignaturePatterns (#1734) The pattern for `void method(org.package.ClassA arg);`. --- .../gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt index 6a32c7c55..ec451ebdc 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt @@ -39,6 +39,8 @@ class RelocatorRemapperTest { Arguments.of("Lorg/package/ClassA;Lorg/package/ClassB;", "Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;"), // Multiple classes. Arguments.of("Ljava/lang/Object;Lorg/package/ClassB;", "Ljava/lang/Object;Lshadow/org/package/ClassB;"), + // Single method argument. + Arguments.of("(Lorg/package/ClassA;)", "(Lshadow/org/package/ClassA;)"), // Method arguments. Arguments.of("(Lorg/package/ClassA;Lorg/package/ClassB;)", "(Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;)"), // Method return types. From dbc72f8668e135a7f98634046e34b4c991405397 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 12 Sep 2025 22:37:46 +0800 Subject: [PATCH 747/941] Tidy up RelocatorRemapperTest (#1735) --- .../relocation/RelocatorRemapperTest.kt | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt index ec451ebdc..6ce670cce 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt @@ -9,7 +9,7 @@ import org.junit.jupiter.params.provider.MethodSource class RelocatorRemapperTest { @ParameterizedTest - @MethodSource("classSignatureStringConstants") + @MethodSource("signaturePatternsProvider") fun relocateSignaturePatterns(input: String, expected: String) { val relocator = RelocatorRemapper( relocators = setOf( @@ -22,29 +22,29 @@ class RelocatorRemapperTest { private companion object { val primitiveTypes = setOf('B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z') - val primitiveTypeArguments = primitiveTypes.map { + val primitiveTypePatterns = primitiveTypes.map { // Methods like `void method(boolean arg1, org.package.ClassA arg2)` Arguments.of("(${it}Lorg/package/ClassA;)V", "(${it}Lshadow/org/package/ClassA;)V") } @JvmStatic - fun classSignatureStringConstants() = listOf( - // Normal class. + fun signaturePatternsProvider() = listOf( + // Normal class: `org.package.ClassA` Arguments.of("Lorg/package/ClassA;", "Lshadow/org/package/ClassA;"), - // Array class. + // Array class: `org.package.ClassA[]` Arguments.of("[Lorg/package/ClassA;", "[Lshadow/org/package/ClassA;"), - // Multidimensional array of class. + // Multidimensional array of class: `org.package.ClassA[][]` Arguments.of("[[Lorg/package/ClassA;", "[[Lshadow/org/package/ClassA;"), - // Multiple classes. + // Multiple classes: `org.package.ClassA org.package.ClassB` Arguments.of("Lorg/package/ClassA;Lorg/package/ClassB;", "Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;"), - // Multiple classes. + // Multiple classes: `java.lang.Object org.package.ClassB` Arguments.of("Ljava/lang/Object;Lorg/package/ClassB;", "Ljava/lang/Object;Lshadow/org/package/ClassB;"), - // Single method argument. + // Single method argument: `void method(org.package.ClassA arg);` Arguments.of("(Lorg/package/ClassA;)", "(Lshadow/org/package/ClassA;)"), - // Method arguments. + // Method arguments: `void method(org.package.ClassA arg1, org.package.ClassB arg2);` Arguments.of("(Lorg/package/ClassA;Lorg/package/ClassB;)", "(Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;)"), - // Method return types. + // Example from issue 1403. Arguments.of("()Lorg/package/ClassA;Lorg/package/ClassB;", "()Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;"), - ) + primitiveTypeArguments + ) + primitiveTypePatterns } } From 7acc20ede56f91f22e8ca01672bea56376f75647 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 14 Sep 2025 10:41:00 +0800 Subject: [PATCH 748/941] Update dependency org.xmlunit:xmlunit-legacy to v2.10.4 (#1736) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 4519d1cbf..5f831255a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,7 +16,7 @@ jdom2 = "org.jdom:jdom2:2.0.6.1" kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" -xmlunit = "org.xmlunit:xmlunit-legacy:2.10.3" +xmlunit = "org.xmlunit:xmlunit-legacy:2.10.4" moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } From c2cd2ced073e990174d57e33f0555c95a489dd46 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 14 Sep 2025 16:56:59 +0800 Subject: [PATCH 749/941] Drop start script templates and clean up ShadowApplicationPlugin (#1738) Syncs with https://github.com/gradle/gradle/blob/fdecc3c95828bb9a1c1bb6114483fe5b16f9159d/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java. * Remove redundant `Set permissions for the start scripts` * Remove redundant `unixStartScript` and `windowsStartScript` * Remove `requireResourceAsText` * Cleanups * Honor `executableDir` in `configureDistribution` * Check `executableDir` in `configureInstallTask` * Clean up `Check installation directory` * Update changelog * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/update-start-scripts.yml | 36 --- docs/changes/README.md | 2 + .../shadow/transformers/TransformersTest.kt | 5 +- .../plugins/shadow/ShadowApplicationPlugin.kt | 40 +-- .../gradle/plugins/shadow/internal/Utils.kt | 4 - .../shadow/internal/unixStartScript.txt | 299 ------------------ .../shadow/internal/windowsStartScript.txt | 97 ------ 7 files changed, 19 insertions(+), 464 deletions(-) delete mode 100644 .github/workflows/update-start-scripts.yml delete mode 100644 src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt delete mode 100644 src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt diff --git a/.github/workflows/update-start-scripts.yml b/.github/workflows/update-start-scripts.yml deleted file mode 100644 index b91a9ccb2..000000000 --- a/.github/workflows/update-start-scripts.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Update Start Scripts - -on: - push: - branches: - - main - paths: - # There's no need to run the update task frequently; the start scripts are typically updated with Gradle releases. - - gradle/wrapper/** - workflow_dispatch: - -jobs: - update-start-scripts: - runs-on: ubuntu-24.04-arm - if: github.event.repository.fork == false - steps: - - uses: actions/checkout@v5 - - uses: actions/setup-java@v5 - with: - distribution: 'zulu' - java-version: 21 - - uses: gradle/actions/setup-gradle@v4 - with: - cache-read-only: true - - run: ./gradlew downloadStartScripts - - uses: peter-evans/create-pull-request@v7 - with: - commit-message: | - Update start script templates to the latest - - _Auto-generated by `Update Start Scripts` Github workflow._ - title: Update start script templates to the latest - body: _Auto-generated by `Update Start Scripts` Github workflow._ - branch: actions/update-start-scripts - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> # Same as `committer`. - delete-branch: true diff --git a/docs/changes/README.md b/docs/changes/README.md index c210100a6..39daa13c5 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -15,6 +15,7 @@ mainClass = "my.Main" } ``` +- Honor `executableDir` in `application` extension. ([#1738](https://github.com/GradleUp/shadow/pull/1738)) ### Changed @@ -26,6 +27,7 @@ - Deprecate `InheritManifest`. ([#1722](https://github.com/GradleUp/shadow/pull/1722)) - Use default `JavaExec` error message when main class is not set. ([#1725](https://github.com/GradleUp/shadow/pull/1725)) - Update `RelocatorRemapper` class pattern to cover more Java method descriptors. ([#1731](https://github.com/GradleUp/shadow/pull/1731)) +- Stop using start script templates bundled in Shadow. ([#1738](https://github.com/GradleUp/shadow/pull/1738)) ### Fixed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 1f029f94b..d25b713a3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -6,7 +6,7 @@ import assertk.assertions.isEqualTo import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText +import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.util.containsOnly @@ -15,6 +15,7 @@ import com.github.jengelman.gradle.plugins.shadow.util.getStream import java.util.jar.Attributes as JarAttribute import kotlin.io.path.appendText import kotlin.io.path.invariantSeparatorsPathString +import kotlin.io.path.readText import kotlin.io.path.writeText import kotlin.reflect.KClass import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE @@ -61,7 +62,7 @@ class TransformersTest : BaseTransformerTest() { ) @Test fun mergeLog4j2PluginCacheFiles() { - val content = requireResourceAsText(PLUGIN_CACHE_FILE) + val content = requireResourceAsPath(PLUGIN_CACHE_FILE).readText() val one = buildJarOne { insert(PLUGIN_CACHE_FILE, content) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 04834b9e6..e278d2a63 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -6,8 +6,8 @@ import com.github.jengelman.gradle.plugins.shadow.internal.applicationExtension import com.github.jengelman.gradle.plugins.shadow.internal.distributions import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension import com.github.jengelman.gradle.plugins.shadow.internal.javaToolchainService -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsText import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shadowJar +import java.io.IOException import org.gradle.api.GradleException import org.gradle.api.Plugin import org.gradle.api.Project @@ -17,12 +17,11 @@ import org.gradle.api.tasks.Sync import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.application.CreateStartScripts -import org.gradle.jvm.application.scripts.TemplateBasedScriptGenerator /** * A [Plugin] which packages and runs a project as a Java Application using the shadowed jar. * - * Modified from [org.gradle.api.plugins.ApplicationPlugin.java](https://github.com/gradle/gradle/blob/45a20d82b623786d19b50185e595adf3d7b196b2/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java). + * Modified from [org.gradle.api.plugins.ApplicationPlugin.java](https://github.com/gradle/gradle/blob/fdecc3c95828bb9a1c1bb6114483fe5b16f9159d/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java). * * @see [ApplicationPlugin] */ @@ -47,7 +46,6 @@ public abstract class ShadowApplicationPlugin : Plugin { task.mainClass.convention(mainClass) task.jvmArguments.convention(provider { applicationDefaultJvmArgs }) } - task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) task.javaLauncher.convention(javaToolchainService.launcherFor(javaPluginExtension.toolchain)) } @@ -58,12 +56,6 @@ public abstract class ShadowApplicationPlugin : Plugin { task.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" task.group = ApplicationPlugin.APPLICATION_GROUP - val dir = "com/github/jengelman/gradle/plugins/shadow/internal" - (task.unixStartScriptGenerator as TemplateBasedScriptGenerator).template = - resources.text.fromString(requireResourceAsText("$dir/unixStartScript.txt")) - (task.windowsStartScriptGenerator as TemplateBasedScriptGenerator).template = - resources.text.fromString(requireResourceAsText("$dir/windowsStartScript.txt")) - task.classpath = files(tasks.shadowJar) with(applicationExtension) { @@ -74,7 +66,6 @@ public abstract class ShadowApplicationPlugin : Plugin { task.conventionMapping.map("executableDir", ::getExecutableDir) task.conventionMapping.map("defaultJvmOpts", ::getApplicationDefaultJvmArgs) } - task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) } } @@ -82,29 +73,24 @@ public abstract class ShadowApplicationPlugin : Plugin { protected open fun Project.configureInstallTask() { tasks.installShadowDist.configure { task -> val applicationName = providers.provider { applicationExtension.applicationName } + val executableDir = providers.provider { applicationExtension.executableDir } task.doFirst("Check installation directory") { + val destinationDir = task.destinationDir + val children = destinationDir.list() ?: throw IOException("Could not list directory $destinationDir") + if (children.isEmpty()) return@doFirst if ( - !task.destinationDir.listFiles().isNullOrEmpty() && - ( - !task.destinationDir.resolve("lib").isDirectory || - !task.destinationDir.resolve("bin").isDirectory - ) + !destinationDir.resolve("lib").isDirectory || + !destinationDir.resolve("bin").isDirectory || + !destinationDir.resolve(executableDir.get()).isDirectory ) { throw GradleException( - "The specified installation directory '${task.destinationDir}' is neither empty nor does it contain an installation for '${applicationName.get()}'.\n" + + "The specified installation directory '$destinationDir' is neither empty nor does it contain an installation for '${applicationName.get()}'.\n" + "If you really want to install to this directory, delete it and run the install task again.\n" + "Alternatively, choose a different installation directory.", ) } } - task.doLast("Set permissions for the start scripts") { - task.eachFile { - if (it.path == "bin/${applicationName.get()}") { - it.permissions { permissions -> permissions.unix(UNIX_SCRIPT_PERMISSIONS) } - } - } - } } } @@ -114,11 +100,13 @@ public abstract class ShadowApplicationPlugin : Plugin { shadowDist.from(file("src/dist")) shadowDist.into("lib") { lib -> lib.from(tasks.shadowJar) + // Reflects the value of the `Class-Path` attribute in the JAR manifest. lib.from(configurations.shadow) } - shadowDist.into("bin") { bin -> + // Defaults to bin dir. + shadowDist.into(applicationExtension.executableDir) { bin -> bin.from(tasks.startShadowScripts) - bin.filePermissions { it.unix(UNIX_SCRIPT_PERMISSIONS) } + bin.filePermissions { permissions -> permissions.unix(UNIX_SCRIPT_PERMISSIONS) } } shadowDist.with(applicationExtension.applicationDistribution) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 7f692ed55..0acc755eb 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -60,10 +60,6 @@ internal fun Properties.inputStream( return os.toByteArray().inputStream() } -internal fun requireResourceAsText(name: String): String { - return requireResourceAsStream(name).bufferedReader().use { it.readText() } -} - internal fun requireResourceAsStream(name: String): InputStream { return Utils::class.java.classLoader.getResourceAsStream(name) ?: throw NoSuchFileException("Resource $name not found.") diff --git a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt deleted file mode 100644 index 1358dd7b4..000000000 --- a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/unixStartScript.txt +++ /dev/null @@ -1,299 +0,0 @@ -#!/bin/sh - -# -# Copyright © 2015 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# ${applicationName} start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh ${applicationName} -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions «\$var», «\${var}», «\${var:-default}», «\${var+SET}», -# «\${var#prefix}», «\${var%suffix}», and «\$( cmd )»; -# * compound commands having a testable exit status, especially «case»; -# * various built-in commands including «command», «set», and «ulimit». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "\$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and ${optsEnvironmentVar}) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -#<% /* -# ... and if you're reading this, this IS the template just mentioned. -# -# This template is processed by -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/java/org/gradle/api/internal/plugins/UnixStartScriptGenerator.java -# -# Gradle is a meta-build system used by the project that you're building -# or installing. It's like autoconf but for projects that are written in -# Java and related languages. It's also used to build parts of the Gradle -# project itself. -# -# The Groovy template language is run in two phases. -# -# 1. Any character following \ is passed unmodified through to the -# next phase, while the \ is removed. Any other $ followed by -# varName or {varName} is replaced by the value of that variable. -# -# 2. The result of the first phase is parsed and run in a similar -# manner to JSP or MASON or PHP: anything within < % ... % > is a -# code block, anything else is sent as output, subject to the -# flow imposed by any code segments. -# -# 3. The "output" is a POSIX shell script, which has its own ideas about -# escaping with backslashes, so to get «\» you need to write «\\\\» -# and to get «$» you need to write «\\\$». -# -# For more details about the Groovy Template Engine, see -# https://docs.groovy-lang.org/next/html/documentation/ section §3.15 -# (Template Engines) for details. -# -# (An example invocation of this template is from -# https://github.com/gradle/gradle/blob/HEAD/subprojects/build-init/src/main/java/org/gradle/api/tasks/wrapper/Wrapper.java -# within the Gradle project, which builds "gradlew".) -# */ %> -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: \$0 may be a link -app_path=\$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=\${app_path%"\${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "\$app_path" ] -do - ls=\$( ls -ld "\$app_path" ) - link=\${ls#*' -> '} - case \$link in #( - /*) app_path=\$link ;; #( - *) app_path=\$APP_HOME\$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=\${0##*/} -# Discard cd standard output in case \$CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=\$( cd -P "\${APP_HOME:-./}${appHomeRelativePath}" > /dev/null && printf '%s\\n' "\$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "\$*" -} >&2 - -die () { - echo - echo "\$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "\$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -<% if ( classpath ) {%>\ -CLASSPATH=$classpath -<% } %>\ -<% if ( mainClassName.startsWith('--module ') ) { %> -MODULE_PATH=$modulePath -<% } %> - -# Determine the Java command to use to start the JVM. -if [ -n "\$JAVA_HOME" ] ; then - if [ -x "\$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=\$JAVA_HOME/jre/sh/java - else - JAVACMD=\$JAVA_HOME/bin/java - fi - if [ ! -x "\$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: \$JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "\$cygwin" && ! "\$darwin" && ! "\$nonstop" ; then - case \$MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=\$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case \$MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "\$MAX_FD" || - warn "Could not set maximum file descriptor limit to \$MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and ${optsEnvironmentVar} environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "\$cygwin" || "\$msys" ; then - APP_HOME=\$( cygpath --path --mixed "\$APP_HOME" ) -<% if ( classpath ) {%>\ - CLASSPATH=\$( cygpath --path --mixed "\$CLASSPATH" ) -<% } %>\ -<% if ( mainClassName.startsWith('--module ') ) { %> MODULE_PATH=\$( cygpath --path --mixed "\$MODULE_PATH" )<% } %> - JAVACMD=\$( cygpath --unix "\$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case \$arg in #( - -*) false ;; # don't mess with options #( - /?*) t=\${arg#/} t=/\${t%%/*} # looks like a POSIX filepath - [ -e "\$t" ] ;; #( - *) false ;; - esac - then - arg=\$( cygpath --path --ignore --mixed "\$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "\$@" "\$arg" # push replacement arg - done -fi - -<% /* -# The DEFAULT_JVM_OPTS variable is intentionally defined here to allow using cygwin-processed APP_HOME. -# So far the only way to inject APP_HOME reference into DEFAULT_JVM_OPTS is to post-process the start script; the declaration is a good anchor to do that. -*/ %> -# Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. -DEFAULT_JVM_OPTS=${defaultJvmOpts} - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect \${Hostname} to be expanded, as it is an environment variable and will be -# treated as '\${Hostname}' itself on the command line. - -set -- \\ -<% if ( appNameSystemProperty ) { - %> "-D${appNameSystemProperty}=\$APP_BASE_NAME" \\ -<% } %>\ -<% if ( classpath ) {%>\ - -classpath "\$CLASSPATH" \\ -<% } %>\ -<% if ( mainClassName.startsWith('--module ') ) { - %> --module-path "\$MODULE_PATH" \\ -<% } %> ${mainClassName ?: entryPointArgs} \\ - "\$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"\$var" ) && -# set -- "\${ARGS[@]}" "\$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- \$( - printf '%s\\n' "\$DEFAULT_JVM_OPTS \$JAVA_OPTS \$${optsEnvironmentVar}" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' | - tr '\\n' ' ' - )" '"\$@"' - -exec "\$JAVACMD" "\$@" diff --git a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt b/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt deleted file mode 100644 index 82e7fa8a4..000000000 --- a/src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal/windowsStartScript.txt +++ /dev/null @@ -1,97 +0,0 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem -@rem SPDX-License-Identifier: Apache-2.0 -@rem - -@if "%DEBUG%"=="" @echo off -@rem ########################################################################## -@rem -@rem ${applicationName} startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%"=="" set DIRNAME=.\ - -@rem This is normally unused -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME%${appHomeRelativePath} - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and ${optsEnvironmentVar} to pass JVM options to this script. -set DEFAULT_JVM_OPTS=${defaultJvmOpts} - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if %ERRORLEVEL% equ 0 goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. 1>&2 -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 -echo. 1>&2 -echo Please set the JAVA_HOME variable in your environment to match the 1>&2 -echo location of your Java installation. 1>&2 - -goto fail - -:execute -@rem Setup the command line - -<% if ( classpath ) {%>\ -set CLASSPATH=$classpath -<% } %>\ -<% if ( mainClassName.startsWith('--module ') ) { %>set MODULE_PATH=$modulePath<% } %> - -@rem Execute ${applicationName} -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %${optsEnvironmentVar}% <% if ( appNameSystemProperty ) { %>"-D${appNameSystemProperty}=%APP_BASE_NAME%"<% } %><% if ( classpath ) {%> -classpath "%CLASSPATH%"<% } %> <% if ( mainClassName.startsWith('--module ') ) { %>--module-path "%MODULE_PATH%" <% } %>${mainClassName ?: entryPointArgs} %* - -:end -@rem End local scope for the variables with windows NT shell -if %ERRORLEVEL% equ 0 goto mainEnd - -:fail -rem Set variable ${exitEnvironmentVar} if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -set EXIT_CODE=%ERRORLEVEL% -if %EXIT_CODE% equ 0 set EXIT_CODE=1 -if not ""=="%${exitEnvironmentVar}%" exit %EXIT_CODE% -exit /b %EXIT_CODE% - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega From 53bd00b9bcbd17c650a8737d6ad0efe97723eb9e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 14 Sep 2025 18:16:38 +0800 Subject: [PATCH 750/941] Suppress usages of conventionMapping (#1739) --- gradle/lint-baseline.xml | 44 ------------------- .../plugins/shadow/ShadowApplicationPlugin.kt | 1 + 2 files changed, 1 insertion(+), 44 deletions(-) diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index d22512c46..398b73d9c 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -78,50 +78,6 @@ column="14"/> - - - - - - - - - - - - - - - - { task.classpath = files(tasks.shadowJar) + @Suppress("InternalGradleApiUsage") // Usages of conventionMapping. with(applicationExtension) { task.mainModule.convention(mainModule) task.mainClass.convention(mainClass) From cb6ed4ceb73e5bea264b659369402addcbee6591 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 15 Sep 2025 10:38:51 +0800 Subject: [PATCH 751/941] Honor executableDir and applicationName in application extension (#1740) * Set `distributionBaseName` * Test `honorApplicationExtensionProperties` * Defer `getExecutableDir` * Check distribution entries * Append suffix to `applicationName` * Update changelog --- docs/changes/README.md | 3 +- .../plugins/shadow/ApplicationPluginTest.kt | 30 +++++++++++++++++++ .../plugins/shadow/ShadowApplicationPlugin.kt | 15 +++++++--- 3 files changed, 43 insertions(+), 5 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 39daa13c5..0ff283ca0 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -15,7 +15,8 @@ mainClass = "my.Main" } ``` -- Honor `executableDir` in `application` extension. ([#1738](https://github.com/GradleUp/shadow/pull/1738)) +- Honor `executableDir` and `applicationName` in `application` extension. ([#1740](https://github.com/GradleUp/shadow/pull/1738)) + This is useful when you want to customize the output directory of the start scripts and the application distribution. ### Changed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 964fb5349..03ba0cf38 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -258,6 +258,36 @@ class ApplicationPluginTest : BasePluginTest() { } } + @Test + fun honorApplicationExtensionProperties() { + val applicationNames = "new" to "new" + val executableDirs = "sbin" to "sbin" + + prepare( + applicationBlock = """ + applicationName = '${applicationNames.first}' + executableDir = '${executableDirs.first}' + """.trimIndent(), + ) + + run(installShadowDistPath, shadowDistZipPath) + + assertThat(path("build/install/").walkEntries()).containsOnly( + "${applicationNames.second}-shadow/${executableDirs.second}/${applicationNames.second}", + "${applicationNames.second}-shadow/${executableDirs.second}/${applicationNames.second}.bat", + "${applicationNames.second}-shadow/lib/myapp-1.0-all.jar", + ) + val zipPath = path("build/distributions/${applicationNames.second}-shadow-1.0.zip") + ZipFile(zipPath.toFile()).use { zip -> + val entries = zip.entries().toList().filter { !it.isDirectory }.map { it.name } + assertThat(entries).containsOnly( + "${applicationNames.second}-shadow-1.0/${executableDirs.second}/${applicationNames.second}", + "${applicationNames.second}-shadow-1.0/${executableDirs.second}/${applicationNames.second}.bat", + "${applicationNames.second}-shadow-1.0/lib/myapp-1.0-all.jar", + ) + } + } + private fun prepare( mainClassWithImports: Boolean = false, projectBlock: String = "", diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 212f54d13..e75395564 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -44,7 +44,7 @@ public abstract class ShadowApplicationPlugin : Plugin { with(applicationExtension) { task.mainModule.convention(mainModule) task.mainClass.convention(mainClass) - task.jvmArguments.convention(provider { applicationDefaultJvmArgs }) + task.jvmArguments.convention(provider(::getApplicationDefaultJvmArgs)) } task.modularity.inferModulePath.convention(javaPluginExtension.modularity.inferModulePath) task.javaLauncher.convention(javaToolchainService.launcherFor(javaPluginExtension.toolchain)) @@ -73,8 +73,8 @@ public abstract class ShadowApplicationPlugin : Plugin { protected open fun Project.configureInstallTask() { tasks.installShadowDist.configure { task -> - val applicationName = providers.provider { applicationExtension.applicationName } - val executableDir = providers.provider { applicationExtension.executableDir } + val applicationName = provider(applicationExtension::getApplicationName) + val executableDir = provider(applicationExtension::getExecutableDir) task.doFirst("Check installation directory") { val destinationDir = task.destinationDir @@ -97,6 +97,13 @@ public abstract class ShadowApplicationPlugin : Plugin { protected open fun Project.configureDistribution() { distributions.register(DISTRIBUTION_NAME) { + it.distributionBaseName.convention( + provider { + // distributionBaseName defaults to `$project.name-$distribution.name`, applicationName defaults to project.name + // so we append the suffix to match the default distributionBaseName. Modified from `ApplicationPlugin.configureDistribution()`. + "${applicationExtension.applicationName}-$DISTRIBUTION_NAME" + }, + ) it.contents { shadowDist -> shadowDist.from(file("src/dist")) shadowDist.into("lib") { lib -> @@ -105,7 +112,7 @@ public abstract class ShadowApplicationPlugin : Plugin { lib.from(configurations.shadow) } // Defaults to bin dir. - shadowDist.into(applicationExtension.executableDir) { bin -> + shadowDist.into(provider(applicationExtension::getExecutableDir)) { bin -> bin.from(tasks.startShadowScripts) bin.filePermissions { permissions -> permissions.unix(UNIX_SCRIPT_PERMISSIONS) } } From 9607b5a76136f06614d46266b9de703715e3c675 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 15 Sep 2025 10:49:48 +0800 Subject: [PATCH 752/941] Rename dist and spec --- .../plugins/shadow/ShadowApplicationPlugin.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index e75395564..f5b05ad69 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -96,27 +96,27 @@ public abstract class ShadowApplicationPlugin : Plugin { } protected open fun Project.configureDistribution() { - distributions.register(DISTRIBUTION_NAME) { - it.distributionBaseName.convention( + distributions.register(DISTRIBUTION_NAME) { dist -> + dist.distributionBaseName.convention( provider { // distributionBaseName defaults to `$project.name-$distribution.name`, applicationName defaults to project.name // so we append the suffix to match the default distributionBaseName. Modified from `ApplicationPlugin.configureDistribution()`. "${applicationExtension.applicationName}-$DISTRIBUTION_NAME" }, ) - it.contents { shadowDist -> - shadowDist.from(file("src/dist")) - shadowDist.into("lib") { lib -> + dist.contents { distSpec -> + distSpec.from(file("src/dist")) + distSpec.into("lib") { lib -> lib.from(tasks.shadowJar) // Reflects the value of the `Class-Path` attribute in the JAR manifest. lib.from(configurations.shadow) } // Defaults to bin dir. - shadowDist.into(provider(applicationExtension::getExecutableDir)) { bin -> + distSpec.into(provider(applicationExtension::getExecutableDir)) { bin -> bin.from(tasks.startShadowScripts) bin.filePermissions { permissions -> permissions.unix(UNIX_SCRIPT_PERMISSIONS) } } - shadowDist.with(applicationExtension.applicationDistribution) + distSpec.with(applicationExtension.applicationDistribution) } } } From fd1e6f46b106ad50a699074ca2e65fa65c241ba5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 15 Sep 2025 11:35:17 +0800 Subject: [PATCH 753/941] Tweak walking entries (#1741) --- .../gradle/plugins/shadow/ApplicationPluginTest.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 03ba0cf38..1cf0f0b8a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -16,11 +16,11 @@ import com.github.jengelman.gradle.plugins.shadow.util.getContent import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.isWindows import com.github.jengelman.gradle.plugins.shadow.util.runProcess -import java.nio.file.FileSystems import java.nio.file.Path import java.util.zip.ZipFile import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.appendText +import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.isRegularFile import kotlin.io.path.readText import kotlin.io.path.relativeTo @@ -30,7 +30,6 @@ import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.DisabledOnOs import org.junit.jupiter.api.condition.OS -@ExperimentalPathApi class ApplicationPluginTest : BasePluginTest() { private lateinit var mainClass: String @@ -337,11 +336,10 @@ class ApplicationPluginTest : BasePluginTest() { } private companion object { - fun Path.walkEntries(): Sequence { - return walk() - .filter { it.isRegularFile() } - .map { it.relativeTo(this) } - .map { it.toString().replace(FileSystems.getDefault().separator, "/") } - } + @OptIn(ExperimentalPathApi::class) + fun Path.walkEntries(includeDirs: Boolean = false): Sequence = walk() + .filter { includeDirs || it.isRegularFile() } + .map { it.relativeTo(this) } + .map { it.invariantSeparatorsPathString } } } From 3786ff249d6c349fe2b9c72d725ce6e569bb10f1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 15 Sep 2025 11:54:57 +0800 Subject: [PATCH 754/941] Sync jvmTarget from jdkRelease (#1742) --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index d7be3d25f..ee0883ecd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,7 +39,7 @@ kotlin { // https://docs.gradle.org/current/userguide/compatibility.html#kotlin apiVersion = KotlinVersion.KOTLIN_2_0 languageVersion = apiVersion - jvmTarget = JvmTarget.JVM_11 + jvmTarget = JvmTarget.fromTarget(libs.versions.jdkRelease.get()) jvmDefault = JvmDefaultMode.NO_COMPATIBILITY freeCompilerArgs.add("-Xjdk-release=${libs.versions.jdkRelease.get()}") } From d15578ef646c4d61543f41b09f93cfe3694b43bc Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 15 Sep 2025 12:17:36 +0800 Subject: [PATCH 755/941] Bump min Java requirement to 17 (#1744) --- README.md | 1 + docs/README.md | 1 + docs/changes/README.md | 1 + gradle/libs.versions.toml | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f5d791223..5bd0aa91a 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. | 8.0.0+ | 8.0 | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | | 9.0.0+ | 8.11 | 11 | [`com.gradleup.shadow`][gradleup's] | +| 9.2.0+ | 8.11 | 17 | [`com.gradleup.shadow`][gradleup's] | diff --git a/docs/README.md b/docs/README.md index 0a8e4f010..dd7b0e6be 100644 --- a/docs/README.md +++ b/docs/README.md @@ -28,6 +28,7 @@ dependent libraries into the output jar without incurring the I/O overhead of ex | 8.0.0+ | 8.0 | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | | 9.0.0+ | 8.11 | 11 | [`com.gradleup.shadow`][gradleup's] | +| 9.2.0+ | 8.11 | 17 | [`com.gradleup.shadow`][gradleup's] | ## Benefits of Shadow diff --git a/docs/changes/README.md b/docs/changes/README.md index 0ff283ca0..bea3e047e 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -29,6 +29,7 @@ - Use default `JavaExec` error message when main class is not set. ([#1725](https://github.com/GradleUp/shadow/pull/1725)) - Update `RelocatorRemapper` class pattern to cover more Java method descriptors. ([#1731](https://github.com/GradleUp/shadow/pull/1731)) - Stop using start script templates bundled in Shadow. ([#1738](https://github.com/GradleUp/shadow/pull/1738)) +- Bump min Java requirement to 17. ([#1744](https://github.com/GradleUp/shadow/pull/1744)) ### Fixed diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5f831255a..7e7e00f6e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -jdkRelease = "11" +jdkRelease = "17" kotlin = "2.2.20" moshi = "1.15.2" pluginPublish = "2.0.0" From 3d03c4399e06746b224971dd1a4914b09ac4b793 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 15 Sep 2025 12:27:49 +0800 Subject: [PATCH 756/941] Junit 6.0.0-RC3 (#1743) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7e7e00f6e..80c4f956e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.7.1" -junit-bom = "org.junit:junit-bom:5.13.4" +junit-bom = "org.junit:junit-bom:6.0.0-RC3" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From d5477baa25ed263e545760861e113580524cdc95 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 15 Sep 2025 13:27:03 +0800 Subject: [PATCH 757/941] Require most optional properties non-null (#1745) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 1 + .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 1 - .../shadow/transformers/ApacheNoticeResourceTransformer.kt | 6 ++---- .../plugins/shadow/transformers/AppendingTransformer.kt | 6 ++---- .../shadow/transformers/DontIncludeResourceTransformer.kt | 6 ++---- .../shadow/transformers/ManifestResourceTransformer.kt | 7 ++----- .../plugins/shadow/transformers/XmlAppendingTransformer.kt | 6 ++---- 7 files changed, 11 insertions(+), 22 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index bea3e047e..3869ffde1 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -30,6 +30,7 @@ - Update `RelocatorRemapper` class pattern to cover more Java method descriptors. ([#1731](https://github.com/GradleUp/shadow/pull/1731)) - Stop using start script templates bundled in Shadow. ([#1738](https://github.com/GradleUp/shadow/pull/1738)) - Bump min Java requirement to 17. ([#1744](https://github.com/GradleUp/shadow/pull/1744)) +- Require most optional properties non-null. ([#1745](https://github.com/GradleUp/shadow/pull/1745)) ### Fixed diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 449b88c69..f988a1d1f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -133,7 +133,6 @@ public abstract class ShadowJar : Jar() { * Defaults to a set that contains `runtimeClasspath` or `runtime` configuration. */ @get:Classpath - @get:Optional public open val configurations: SetProperty = objectFactory.setProperty() @get:Input diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 8e3e50add..40109113a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -13,7 +13,6 @@ import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional /** * Merges `META-INF/NOTICE.TXT` files. @@ -67,9 +66,8 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( @get:Input public open val inceptionYear: Property = objectFactory.property("2006") - @get:Optional @get:Input - public open val copyright: Property = objectFactory.property() + public open val copyright: Property = objectFactory.property("") /** * The file encoding of the `NOTICE` file. @@ -160,7 +158,7 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( override fun hasTransformedResource(): Boolean = true override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { - val copyright = copyright.orNull ?: fallbackCopyright + val copyright = copyright.get().ifEmpty { fallbackCopyright } val sb = StringBuilder() var count = 0 for (line in entries) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index 56a35be5f..b65ebd2fd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -9,7 +9,6 @@ import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional /** * A resource processor that appends content for a resource, separated by a newline. @@ -26,15 +25,14 @@ public open class AppendingTransformer @Inject constructor( private var _data: ByteArrayOutputStream? = null // It's nullable to allow lazy initialization to support CC. private inline val data get() = _data ?: ByteArrayOutputStream().also { _data = it } - @get:Optional @get:Input - public open val resource: Property = objectFactory.property() + public open val resource: Property = objectFactory.property("") @get:Input public open val separator: Property = objectFactory.property(DEFAULT_SEPARATOR) override fun canTransformResource(element: FileTreeElement): Boolean { - return resource.orNull.equals(element.path, ignoreCase = true) + return resource.get().equals(element.path, ignoreCase = true) } override fun transform(context: TransformerContext) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index 4496d1528..308913552 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -7,7 +7,6 @@ import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional /** * A resource processor that prevents the inclusion of an arbitrary resource into the shaded JAR. @@ -22,11 +21,10 @@ import org.gradle.api.tasks.Optional public open class DontIncludeResourceTransformer @Inject constructor( final override val objectFactory: ObjectFactory, ) : ResourceTransformer by ResourceTransformer.Companion { - @get:Optional @get:Input - public open val resource: Property = objectFactory.property() + public open val resource: Property = objectFactory.property("") override fun canTransformResource(element: FileTreeElement): Boolean { - return !resource.orNull.isNullOrEmpty() && element.path.endsWith(resource.get()) + return resource.get().isNotEmpty() && element.path.endsWith(resource.get()) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 285e6eb5a..1dc654593 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -16,7 +16,6 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional /** * A resource processor that allows the arbitrary addition of attributes to @@ -35,11 +34,9 @@ public open class ManifestResourceTransformer @Inject constructor( private var manifestDiscovered = false private var manifest: Manifest? = null - @get:Optional @get:Input - public open val mainClass: Property = objectFactory.property() + public open val mainClass: Property = objectFactory.property("") - @get:Optional @get:Input public open val manifestEntries: MapProperty = objectFactory.mapProperty() @@ -70,7 +67,7 @@ public open class ManifestResourceTransformer @Inject constructor( } val attributes = manifest!!.mainAttributes - mainClass.orNull?.let { + mainClass.get().takeIf(CharSequence::isNotEmpty)?.let { attributes[mainClassAttributeKey] = it } manifestEntries.get().forEach { (key, value) -> diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index 61e1287f1..2e02fd805 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -10,7 +10,6 @@ import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Optional import org.jdom2.Document import org.jdom2.JDOMException import org.jdom2.input.SAXBuilder @@ -36,12 +35,11 @@ public open class XmlAppendingTransformer @Inject constructor( @get:Input public open val ignoreDtd: Property = objectFactory.property(true) - @get:Optional @get:Input - public open val resource: Property = objectFactory.property() + public open val resource: Property = objectFactory.property("") override fun canTransformResource(element: FileTreeElement): Boolean { - return resource.orNull?.equals(element.path, ignoreCase = true) == true + return resource.get().equals(element.path, ignoreCase = true) } override fun transform(context: TransformerContext) { From df48375b68237e065658e7df311550926e3d51e0 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 15 Sep 2025 15:03:31 +0800 Subject: [PATCH 758/941] Tidy up unit tests (#1746) --- .../shadow/relocation/SimpleRelocatorTest.kt | 2 - .../ApacheNoticeResourceTransformerTest.kt | 65 ++----------------- .../transformers/BaseTransformerTest.kt | 2 +- .../ComponentsXmlResourceTransformerTest.kt | 20 +++--- .../Log4j2PluginsCacheFileTransformerTest.kt | 9 ++- 5 files changed, 20 insertions(+), 78 deletions(-) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index f6cf39a52..43374d6ef 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -8,8 +8,6 @@ import org.junit.jupiter.api.Test /** * Modified from [org.apache.maven.plugins.shade.relocation.SimpleRelocatorTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java). - * - * @author John Engelman */ class SimpleRelocatorTest { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt index 75418b594..1a65cb0a7 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -4,8 +4,6 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isFalse import assertk.assertions.isTrue -import assertk.fail -import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream import java.nio.charset.Charset import java.util.zip.ZipInputStream @@ -13,7 +11,7 @@ import org.apache.tools.zip.ZipOutputStream import org.junit.jupiter.api.Test /** - * Modified from [org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerParameterTests.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformerParameterTests.java). + * Modified from [org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformerTest.java). */ class ApacheNoticeResourceTransformerTest : BaseTransformerTest() { @@ -37,71 +35,22 @@ class ApacheNoticeResourceTransformerTest : BaseTransformerTest { transformer = clazz.create(testObjectFactory) } - protected companion object { + companion object { const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" fun ResourceTransformer.canTransformResource(path: String, isFile: Boolean = true): Boolean { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt index 276ac4553..a610285f2 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -13,23 +13,19 @@ class ComponentsXmlResourceTransformerTest : BaseTransformerTest Date: Mon, 15 Sep 2025 16:13:03 +0800 Subject: [PATCH 759/941] Share test kits in a new source set (#1747) --- build.gradle.kts | 6 +++++ .../plugins/shadow/ApplicationPluginTest.kt | 8 +++--- .../gradle/plugins/shadow/BasePluginTest.kt | 4 +-- .../gradle/plugins/shadow/CachingTest.kt | 6 ++--- .../gradle/plugins/shadow/FilteringTest.kt | 2 +- .../gradle/plugins/shadow/GroovyPluginTest.kt | 2 +- .../gradle/plugins/shadow/JavaPluginsTest.kt | 12 ++++----- .../plugins/shadow/KotlinPluginsTest.kt | 6 ++--- .../gradle/plugins/shadow/MinimizeTest.kt | 6 ++--- .../gradle/plugins/shadow/PublishingTest.kt | 10 ++++---- .../gradle/plugins/shadow/RelocationTest.kt | 4 +-- .../gradle/plugins/shadow/ScalaPluginTest.kt | 2 +- .../transformers/AppendingTransformerTest.kt | 2 +- .../GroovyExtensionModuleTransformerTest.kt | 4 +-- .../PropertiesFileTransformerTest.kt | 2 +- .../ServiceFileTransformerTest.kt | 2 +- .../shadow/transformers/TransformersTest.kt | 10 ++++---- .../XmlAppendingTransformerTest.kt | 2 +- .../gradle/plugins/shadow/internal/Utils.kt | 17 ------------- .../transformers/BaseTransformerTest.kt | 23 ++++++++---------- .../ComponentsXmlResourceTransformerTest.kt | 2 +- .../Log4j2PluginsCacheFileTransformerTest.kt | 6 ++--- .../ManifestAppenderTransformerTest.kt | 9 +++---- .../PropertiesFileTransformerTest.kt | 11 +++------ .../ServiceFileTransformerTest.kt | 23 ++++++------------ .../gradle/plugins/shadow/util/Utils.kt | 2 +- .../core/config/plugins/Log4j2Plugins.dat | Bin 17923 -> 0 bytes .../gradle/plugins/shadow/testkit}/JarPath.kt | 2 +- .../gradle/plugins/shadow/testkit/Resource.kt | 19 +++++++++++++++ .../core/config/plugins/Log4j2Plugins.dat | Bin 30 files changed, 96 insertions(+), 108 deletions(-) delete mode 100644 src/test/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat rename src/{functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util => testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit}/JarPath.kt (97%) create mode 100644 src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Resource.kt rename src/{functionalTest => testKit}/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat (100%) diff --git a/build.gradle.kts b/build.gradle.kts index ee0883ecd..8c1255170 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -66,6 +66,9 @@ val testPluginClasspath by configurations.registering { description = "Plugins used in integration tests could be resolved in classpath." } +val testKit by sourceSets.creating +val testKitImplementation by configurations.getting + configurations.configureEach { when (name) { API_ELEMENTS_CONFIGURATION_NAME, @@ -115,6 +118,8 @@ dependencies { implementation(libs.plexus.utils) implementation(libs.plexus.xml) + testKitImplementation(libs.assertk) + testPluginClasspath(libs.foojayResolver) testPluginClasspath(libs.develocity) testPluginClasspath(libs.kotlin.kmp) @@ -172,6 +177,7 @@ testing.suites { withType().configureEach { useJUnitJupiter(libs.junit.bom.map { requireNotNull(it.version) }) dependencies { + implementation(testKit.output) implementation(libs.assertk) } targets.configureEach { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 1cf0f0b8a..96d56543f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -9,11 +9,11 @@ import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.DISTRIBUTION_NAME import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey +import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath +import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.testkit.getContent +import com.github.jengelman.gradle.plugins.shadow.testkit.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.getContent -import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.isWindows import com.github.jengelman.gradle.plugins.shadow.util.runProcess import java.nio.file.Path 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 549f34ee9..40217f1d9 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 @@ -8,13 +8,13 @@ import assertk.assertions.isEqualTo import assertk.assertions.isNotNull import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_RUN_TASK_NAME -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder -import com.github.jengelman.gradle.plugins.shadow.util.JarPath import com.github.jengelman.gradle.plugins.shadow.util.JvmLang import java.io.Closeable import java.nio.file.Path diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt index d2eba7dd6..f2accb91e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -6,12 +6,12 @@ import assertk.assertions.isEmpty import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey +import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly +import com.github.jengelman.gradle.plugins.shadow.testkit.getMainAttr import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.ServiceFileTransformer import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import kotlin.io.path.ExperimentalPathApi import kotlin.io.path.appendText import kotlin.io.path.isDirectory diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index ff35a1e78..04aa43ede 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -1,8 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeEach diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt index bea1706d9..9b406fefa 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt @@ -1,8 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeEach diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 400d843da..90cea057d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -23,13 +23,13 @@ import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.testkit.containsNone +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly +import com.github.jengelman.gradle.plugins.shadow.testkit.getContent +import com.github.jengelman.gradle.plugins.shadow.testkit.getMainAttr +import com.github.jengelman.gradle.plugins.shadow.testkit.getStream import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.containsNone -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import com.github.jengelman.gradle.plugins.shadow.util.getContent -import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr -import com.github.jengelman.gradle.plugins.shadow.util.getStream import com.github.jengelman.gradle.plugins.shadow.util.runProcess import kotlin.io.path.appendText import kotlin.io.path.invariantSeparatorsPathString diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index ad41a3b9d..1122e76de 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -5,11 +5,11 @@ import assertk.assertions.contains import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly +import com.github.jengelman.gradle.plugins.shadow.testkit.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -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 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index b0fda4b83..c33269841 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -2,10 +2,10 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.testkit.containsNone +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.containsNone -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.Test diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index f3a6c138f..58f3eb93c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -11,14 +11,14 @@ import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowJavaPlugin.Companion.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath +import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.testkit.containsNone +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly +import com.github.jengelman.gradle.plugins.shadow.testkit.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.containsNone -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.coordinate -import com.github.jengelman.gradle.plugins.shadow.util.getMainAttr import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index b4a56bc68..e2e89cd3b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -9,9 +9,9 @@ import assertk.assertions.isNotEmpty import assertk.fail import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.Companion.CONSTANT_TIME_FOR_ZIP_ENTRIES +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly +import com.github.jengelman.gradle.plugins.shadow.testkit.getBytes import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import com.github.jengelman.gradle.plugins.shadow.util.getBytes import com.github.jengelman.gradle.plugins.shadow.util.runProcess import java.net.URLClassLoader import kotlin.io.path.appendText diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt index e732701ae..9be358b36 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt @@ -1,8 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly import kotlin.io.path.appendText import kotlin.io.path.writeText import org.junit.jupiter.api.BeforeEach diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 63bdea05e..a770165b1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -2,7 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo -import com.github.jengelman.gradle.plugins.shadow.util.getContent +import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import kotlin.io.path.appendText import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index 2abdc7d52..6b717adce 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -2,6 +2,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath +import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.KEY_EXTENSION_CLASSES import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.KEY_MODULE_NAME import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.KEY_MODULE_VERSION @@ -10,8 +12,6 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionMo import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.MERGED_MODULE_VERSION import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR -import com.github.jengelman.gradle.plugins.shadow.util.JarPath -import com.github.jengelman.gradle.plugins.shadow.util.getContent import java.nio.file.Path import kotlin.io.path.appendText import org.junit.jupiter.api.Test diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index f7057d3a8..525f7174f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -3,9 +3,9 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 6b1d2fe41..a80d4af57 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -3,8 +3,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.containsMatch import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import kotlin.io.path.writeText import org.gradle.api.file.DuplicatesStrategy diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index d25b713a3..37224074a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -6,12 +6,12 @@ import assertk.assertions.isEqualTo import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath +import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly +import com.github.jengelman.gradle.plugins.shadow.testkit.getContent +import com.github.jengelman.gradle.plugins.shadow.testkit.getStream +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.containsAtLeast -import com.github.jengelman.gradle.plugins.shadow.util.containsOnly -import com.github.jengelman.gradle.plugins.shadow.util.getContent -import com.github.jengelman.gradle.plugins.shadow.util.getStream import java.util.jar.Attributes as JarAttribute import kotlin.io.path.appendText import kotlin.io.path.invariantSeparatorsPathString diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index d35ae4d48..446a9f5c5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -2,8 +2,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.getContent import kotlin.io.path.appendText import org.junit.jupiter.api.Test diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 0acc755eb..708c05efd 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -3,13 +3,9 @@ package com.github.jengelman.gradle.plugins.shadow.internal import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction import java.io.ByteArrayInputStream import java.io.ByteArrayOutputStream -import java.io.InputStream import java.nio.charset.Charset -import java.nio.file.NoSuchFileException -import java.nio.file.Path import java.util.Properties import java.util.jar.Attributes.Name as JarAttributeName -import kotlin.io.path.toPath import org.apache.tools.zip.ZipEntry /** @@ -59,16 +55,3 @@ internal fun Properties.inputStream( } return os.toByteArray().inputStream() } - -internal fun requireResourceAsStream(name: String): InputStream { - return Utils::class.java.classLoader.getResourceAsStream(name) - ?: throw NoSuchFileException("Resource $name not found.") -} - -internal fun requireResourceAsPath(name: String): Path { - val resource = Utils::class.java.classLoader.getResource(name) - ?: throw NoSuchFileException("Resource $name not found.") - return resource.toURI().toPath() -} - -private object Utils diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 6e7373e76..e3232b680 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -1,13 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream +import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath +import com.github.jengelman.gradle.plugins.shadow.testkit.getStream +import com.github.jengelman.gradle.plugins.shadow.testkit.noOpDelegate +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsStream +import com.github.jengelman.gradle.plugins.shadow.testkit.testObjectFactory import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create -import com.github.jengelman.gradle.plugins.shadow.util.noOpDelegate -import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import java.lang.reflect.ParameterizedType -import java.nio.file.Path import java.util.Locale -import java.util.zip.ZipFile import kotlin.io.path.createTempFile import kotlin.io.path.outputStream import org.apache.tools.zip.ZipOutputStream @@ -41,22 +41,19 @@ abstract class BaseTransformerTest { return canTransformResource(element) } - fun readFrom(jarPath: Path, resourceName: String = MANIFEST_NAME): List { - return ZipFile(jarPath.toFile()).use { zip -> - val entry = zip.getEntry(resourceName) ?: return emptyList() - zip.getInputStream(entry).bufferedReader().readLines() - } + fun JarPath.readContentLines(resourceName: String = MANIFEST_NAME): List { + return use { it.getStream(resourceName).bufferedReader().readLines() } } fun doTransformAndGetTransformedPath( transformer: ResourceTransformer, preserveFileTimestamps: Boolean, - ): Path { + ): JarPath { val testableZipPath = createTempFile("testable-zip-file-", ".jar") - ZipOutputStream(testableZipPath.outputStream().buffered()).use { zipOutputStream -> + ZipOutputStream(testableZipPath.outputStream()).use { zipOutputStream -> transformer.modifyOutputStream(zipOutputStream, preserveFileTimestamps) } - return testableZipPath + return JarPath(testableZipPath) } /** diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt index a610285f2..3abea396c 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -2,7 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsStream import org.custommonkey.xmlunit.XMLUnit import org.junit.jupiter.api.Test diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index af582e74f..45ea7c5fb 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -8,11 +8,11 @@ import assertk.assertions.isNotEqualTo import assertk.assertions.isTrue import assertk.assertions.startsWith import assertk.fail -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsPath -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator -import com.github.jengelman.gradle.plugins.shadow.util.zipOutputStream +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsStream +import com.github.jengelman.gradle.plugins.shadow.testkit.zipOutputStream import java.io.ByteArrayOutputStream import java.net.URI import java.net.URL diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt index d4de4d1d3..53a0c690f 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -6,7 +6,7 @@ import assertk.assertions.isFalse import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.internal.requireResourceAsStream +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsStream import org.junit.jupiter.api.Test class ManifestAppenderTransformerTest : BaseTransformerTest() { @@ -44,9 +44,7 @@ class ManifestAppenderTransformerTest : BaseTransformerTest() transformer.modifyOutputStream(zos, false) } - val transformedContent = ZipFile(tempJar.toFile()).use { it.getContent(contentResourceShaded) } + val transformedContent = JarPath(tempJar).use { it.getContent(contentResourceShaded) } assertThat(transformedContent).isEqualTo("borg.foo.Service\norg.foo.exclude.OtherService") } @@ -105,7 +105,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() transformer.modifyOutputStream(zos, false) } - val transformedContent = ZipFile(tempJar.toFile()).use { it.getContent(contentResourceShaded) } + val transformedContent = JarPath(tempJar).use { it.getContent(contentResourceShaded) } assertThat(transformedContent).isEqualTo("borg.foo.Service\norg.foo.exclude.OtherService") } @@ -122,7 +122,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() transformer.modifyOutputStream(zos, false) } - val transformedContent = ZipFile(tempJar.toFile()).use { it.getContent(contentResource) } + val transformedContent = JarPath(tempJar).use { it.getContent(contentResource) } assertThat(transformedContent).isEqualTo("org.eclipse1234.osgi.launch.EquinoxFactory") } @@ -144,7 +144,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() transformer.modifyOutputStream(zos, false) } - val transformedContent = ZipFile(tempJar.toFile()).use { it.getContent(contentResource) } + val transformedContent = JarPath(tempJar).use { it.getContent(contentResource) } assertThat(transformedContent).isEqualTo("borg.foo.Service\norg.blah.Service") } @@ -153,15 +153,6 @@ class ServiceFileTransformerTest : BaseTransformerTest() return TransformerContext(path, input.byteInputStream(), relocators = relocators.toSet()) } - fun ZipFile.getContent(entryName: String): String { - return getStream(entryName).bufferedReader().use { it.readText() } - } - - fun ZipFile.getStream(entryName: String): InputStream { - val entry = requireNotNull(getEntry(entryName)) { "Entry $entryName not found in all entries: ${entries().toList()}" } - return getInputStream(entry) - } - @JvmStatic fun resourceProvider() = listOf( // path, exclude, expected diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt index 0f7168379..53e52179c 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt @@ -1,4 +1,4 @@ -package com.github.jengelman.gradle.plugins.shadow.util +package com.github.jengelman.gradle.plugins.shadow.testkit import java.io.OutputStream import java.lang.reflect.InvocationHandler diff --git a/src/test/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat b/src/test/resources/META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat deleted file mode 100644 index fbe6359f109cd7ee81b48c87d619391f3af4b478..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17923 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 YS_b!C_m3ocyexEz Date: Tue, 16 Sep 2025 04:53:10 +0000 Subject: [PATCH 760/941] Update Gradle to v9.1.0-rc-4 (#1749) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 843452989..0954c8ed5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-rc-3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-rc-4-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From cedff7a4d03e1acc1cf0a10865efe986db9fb8eb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 16 Sep 2025 18:01:40 +0800 Subject: [PATCH 761/941] Remove downloadStartScripts task (#1753) --- build.gradle.kts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 8c1255170..388c74dde 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -246,23 +246,6 @@ tasks.check { ) } -tasks.register("downloadStartScripts") { - description = "Download start scripts from Gradle sources, this should be run intervally to track updates." - - val urlPrefix = "https://raw.githubusercontent.com/gradle/gradle/refs/heads/master/platforms/jvm/" + - "plugins-application/src/main/resources/org/gradle/api/internal/plugins" - from(resources.text.fromUri("$urlPrefix/unixStartScript.txt")) { - rename { "unixStartScript.txt" } - } - from(resources.text.fromUri("$urlPrefix/windowsStartScript.txt")) { - rename { "windowsStartScript.txt" } - } - val destDir = file("src/main/resources/com/github/jengelman/gradle/plugins/shadow/internal").also { - if (!it.exists() || !it.isDirectory) error("Download destination dir $it does not exist or is not a directory.") - } - into(destDir) -} - tasks.clean { delete += listOf( projectDir.resolve(".gradle"), From 1e2c4a857360fe239fd8978b07c6282ef2ec654f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 16 Sep 2025 18:20:48 +0800 Subject: [PATCH 762/941] Dokka 2.1.0-Beta (#1754) https://github.com/Kotlin/dokka/releases/tag/v2.1.0 --- gradle.properties | 5 ----- gradle/libs.versions.toml | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/gradle.properties b/gradle.properties index 250d4ba54..3b6a6ff52 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,11 +9,6 @@ org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g org.gradle.kotlin.dsl.allWarningsAsErrors=true org.gradle.parallel=true -org.jetbrains.dokka.experimental.gradle.pluginMode=V2Enabled -org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=true -org.jetbrains.dokka.experimental.tryK2=true -org.jetbrains.dokka.experimental.tryK2.nowarn=true - ########## Properties for publishing to Maven Central ########## mavenCentralAutomaticPublishing=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 80c4f956e..77a29960f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,7 +35,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.13.0" -jetbrains-dokka = "org.jetbrains.dokka:2.0.0" +jetbrains-dokka = "org.jetbrains.dokka:2.1.0-Beta" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } spotless = "com.diffplug.spotless:7.2.1" From 3c3598c8c993faac54b055fc824736e1694e4030 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 17:13:48 +0000 Subject: [PATCH 763/941] Update Develocity to v4.2 (#1755) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 77a29960f..ba69d8ea4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,7 +21,7 @@ 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:1.0.0" -develocity = "com.gradle:develocity-gradle-plugin:4.1.1" +develocity = "com.gradle:develocity-gradle-plugin:4.2" kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 4cdbd29b4..243f33a45 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "4.1.1" + id("com.gradle.develocity") version "4.2" } develocity { From 07cd46438356c834d4cd4bb6c3dcfb70c96f7e3b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 17 Sep 2025 15:38:45 +0800 Subject: [PATCH 764/941] Override toString for SimpleRelocator (#1757) --- api/shadow.api | 1 + .../plugins/shadow/relocation/SimpleRelocator.kt | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/api/shadow.api b/api/shadow.api index 3ce4bddbf..2a33b3432 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -142,6 +142,7 @@ public class com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocat public fun relocateClass (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocateClassContext;)Ljava/lang/String; public fun relocatePath (Lcom/github/jengelman/gradle/plugins/shadow/relocation/RelocatePathContext;)Ljava/lang/String; public fun setSkipStringConstants (Z)V + public fun toString ()Ljava/lang/String; } public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter : java/io/Serializable { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 931580927..b4519f995 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -163,6 +163,21 @@ public open class SimpleRelocator @JvmOverloads constructor( excludes, ) + override fun toString(): String = buildString { + append("SimpleRelocator(") + append("rawString=$rawString").append(", ") + append("skipStringConstants=$skipStringConstants").append(", ") + append("pattern='$pattern'").append(", ") + append("pathPattern='$pathPattern'").append(", ") + append("shadedPattern='$shadedPattern'").append(", ") + append("shadedPathPattern='$shadedPathPattern'").append(", ") + append("sourcePackageExcludes=$sourcePackageExcludes").append(", ") + append("sourcePathExcludes=$sourcePathExcludes").append(", ") + append("includes=$includes").append(", ") + append("excludes=$excludes") + append(")") + } + private fun isIncluded(path: String): Boolean { return includes.isEmpty() || includes.any { SelectorUtils.matchPath(it, path, "/", true) } } From ab081f0cae10335b9da4d30d868051589a115d82 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 17 Sep 2025 16:47:14 +0800 Subject: [PATCH 765/941] Disable lint for NewerVersionAvailable (#1759) --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index 388c74dde..78f9cc0a2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -50,6 +50,7 @@ lint { ignoreTestFixturesSources = true ignoreTestSources = true warningsAsErrors = true + disable += "NewerVersionAvailable" } spotless { From 1b6d701d452f8747f3f451bc8d2143459bd08789 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 18 Sep 2025 10:48:29 +0800 Subject: [PATCH 766/941] Create copilot-instructions.md with project details (#1761) Added project overview, folder structure, libraries, coding standards, and document guidelines. https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/copilot-instructions.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 000000000..81be83977 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,25 @@ +# Project Overview + +This project is a Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. + +## Folder Structure + +- `/src`: Contains the source code for the main logic and tests. +- `/api`: Contains the API dumps by the Kotlin compiler, which always reflect the public APIs in `/src/main/kotlin`. +- `/docs`: Contains documentation for the project, including API specifications and user guides. They will be generated by MkDocs to serve websites. + +## Libraries and Frameworks + +- Spotless for code style check. +- Dokka for Kdoc and API HTML generation. +- MkDocs for markdown render. +- ASM for modifying bytecode in `RelocatorRemapper`. +- The main logic in `ShadowJar` extends Gradle's `Jar`, which is also a subclass of `Copy` task. + +## Coding Standards + +- Apply indent sizes of 2 for all code. + +## Document guidelines + +- Check and refine the newly added documents and comment wording. From 13d460a27795358d50c5018b06658f6c669a1360 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 18 Sep 2025 19:58:03 +0800 Subject: [PATCH 767/941] Document not relocating Kotlin stdlib and reflect (#1760) --- docs/configuration/relocation/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/configuration/relocation/README.md b/docs/configuration/relocation/README.md index f99fbc0c5..0f64e9b6a 100644 --- a/docs/configuration/relocation/README.md +++ b/docs/configuration/relocation/README.md @@ -210,6 +210,13 @@ To configure automatic dependency relocation, set `enableAutoRelocation = true` > Be mindful that some Gradle plugins will automatically add dependencies to your class path. You may need to remove these > dependencies if you do not intend to shadow them into your library. + +## Relocating Kotlin Standard Library + +It is not recommended to relocate Kotlin Standard Library if you are using [Kotlin Metadata][kotlin-metadata] or +[Kotlin Reflection][kotlin-reflection] in the project, because they are tightly coupled with Kotlin compiler and +runtime. See more details and discussion in [#1622][#1622]. + ## Relocating Project Resources Only If you want to relocate the resources of the project only and exclude all dependencies (related to a normal JAR but with @@ -237,3 +244,9 @@ relocating), you can try out the trick like: This is useful in some cases like [#759](https://github.com/GradleUp/shadow/issues/759) mentioned. See [Configuring Shadowed Dependencies](../dependencies/README.md) for more information about `configurations`. + + + +[#1622]: https://github.com/GradleUp/shadow/issues/1622 +[kotlin-metadata]: https://kotlinlang.org/docs/metadata-jvm.html +[kotlin-reflection]: https://kotlinlang.org/docs/reflection.html From 4078b716702a2c847fc95ed8d626bfe375719e2b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Sep 2025 17:22:24 +0000 Subject: [PATCH 768/941] Update Gradle to v9.1.0 (#1762) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0954c8ed5..2e1113280 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-rc-4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From de1fc287f45826b7ab1ea0d282834343d8cce0ae Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 19 Sep 2025 11:32:06 +0800 Subject: [PATCH 769/941] Remove release notes for 9.0 beta versions (#1763) They are still available on GitHub releases. https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta1 ... https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17 --- docs/changes/README.md | 270 +---------------------------------------- 1 file changed, 4 insertions(+), 266 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 3869ffde1..6988fd582 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -362,6 +362,10 @@ If you used Shadow for merging service files, the following steps are recommende ## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 +!!! note + + Release notes for 9.0.0 beta versions are available on GitHub Releases: https://github.com/GradleUp/shadow/releases + ### Added - Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) @@ -466,272 +470,6 @@ If you used Shadow for merging service files, the following steps are recommende - **BREAKING CHANGE:** Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) - **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) -## [9.0.0-beta17](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta17) - 2025-06-18 - -### Fixed - -- Fix compatibility for Gradle 9.0.0 RC1. ([#1468](https://github.com/GradleUp/shadow/pull/1468)) - -## [9.0.0-beta16](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta16) - 2025-06-14 - -### Changed - -- Update ASM to 9.8 to support Java 25. ([#1380](https://github.com/GradleUp/shadow/pull/1380)) - -### Fixed - -- Restore removed `Named` from `ResourceTransformer`. ([#1449](https://github.com/GradleUp/shadow/pull/1449)) - -## [9.0.0-beta15](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta15) - 2025-05-28 - -### Fixed - -- Pin the plugin's Kotlin language level on 2.0. ([#1448](https://github.com/GradleUp/shadow/pull/1448)) - The language level used in `9.0.0-beta14` is 2.2, which may cause compatibility issues for the plugins depending on - Shadow. - -## [9.0.0-beta14](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta14) - 2025-05-28 - -### Changed - -- Update start script templates. ([#1419](https://github.com/GradleUp/shadow/pull/1419)) -- In-development snapshots are now published to the Central Portal Snapshots repository. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) - -### Fixed - -- Allow using file trees of JARs together with the configuration cache. ([#1441](https://github.com/GradleUp/shadow/pull/1441)) -- Fallback `RelocateClassContext` and `RelocatePathContext` to data classes. ([#1445](https://github.com/GradleUp/shadow/pull/1445)) - -## [9.0.0-beta13](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta13) - 2025-04-29 - -### Changed - -- Set `Main-Class` attr for KMP 1.9.0 or above. ([#1410](https://github.com/GradleUp/shadow/pull/1410)) - -### Fixed - -- Avoid creating jvm targets eagerly for KMP. ([#1378](https://github.com/GradleUp/shadow/pull/1378)) - -## [9.0.0-beta12](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta12) - 2025-04-01 - -### Added - -- Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) - ``` - --enable-relocation Enable relocation of packages in the jar - --no-enable-relocation Disables option --enable-relocation - --minimize-jar Minimize the jar by removing unused classes - --no-minimize-jar Disables option --minimize-jar - --relocation-prefix Prefix to use for relocated packages - --rerun Causes the task to be re-run even if up-to-date - ``` - -### Changed - -- Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) - -## [9.0.0-beta11](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta11) - 2025-03-18 - -### Added - -- 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 for KMP 2.1.0 or above. ([#1337](https://github.com/GradleUp/shadow/pull/1337)) - -### Changed - -- **BREAKING CHANGE:** Polish `ShadowSpec`. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) - - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. - - `ShadowSpec` no longer extends `CopySpec`. - - Overload `relocate`, `transform` and things for better usability in Kotlin. -- **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) -- `runShadow` no longer depends on `installShadowDist`. ([#1353](https://github.com/GradleUp/shadow/pull/1353)) - -### Fixed - -- Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) - -### Removed - -- **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) - -## [9.0.0-beta10](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta10) - 2025-03-05 - -### Added - -- Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) - You still need to manually configure `manifest.attributes` (e.g. `Main-Class` attr) in the `shadowJar` task if necessary. - -### Changed - -- **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) - Aims to better align with the name `org.apache.maven.plugins.shade.resource.ResourceTransformer.java` - and to distinguish itself from `org.gradle.api.Transformer.java`. -- **BREAKING CHANGE:** Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) -- Migrate doc sites to MkDocs. ([#1302](https://github.com/GradleUp/shadow/pull/1302)) - -### Fixed - -- Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) - -### Removed - -- **BREAKING CHANGE:** Remove `Named` from the parents of `Transformer`. ([#1289](https://github.com/GradleUp/shadow/pull/1289)) - -## [9.0.0-beta9](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta9) - 2025-02-24 - -### Added - -- Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) - -### Changed - -- **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) -- Reduce duplicate `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) -- **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) -- **BREAKING CHANGE:** Change the default `duplicatesStrategy` from `EXCLUDE` to `INCLUDE`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - - `ShadowJar` recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. - - Now `ShadowJar` honors `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. -- **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial - issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip - the files, only copy the files as-is. If you still want to shadow the unzipped files, try out something like: - ```kotlin - tasks.shadowJar { - from(zipTree(files('path/to/your/file.zip'))) - } - ``` - or - ```kotlin - dependencies { - implementation(files('path/to/your/file.zip')) - } - ``` -- Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - -### Fixed - -- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -- Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - -### Removed - -- **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) -- **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) -- **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -- **BREAKING CHANGE:** Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) - -## [9.0.0-beta8](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta8) - 2025-02-08 - -### Added - -- Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) -- Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) - -### Changed - -- **BREAKING CHANGE:** Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) -- Bump min Java requirement to 11. ([#1242](https://github.com/GradleUp/shadow/pull/1242)) - -### Removed - -- **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) -- **BREAKING CHANGE:** Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) - -## [9.0.0-beta7](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta7) - 2025-02-02 - -### Added - -- Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) -- Support Java 24. ([#1222](https://github.com/GradleUp/shadow/pull/1222)) - -### Changed - -- Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) -- Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) -- Mark `ShadowJar.dependencyFilter` as `@Input`. ([#1206](https://github.com/GradleUp/shadow/pull/1206)) - `ShadowSpec.stats` is removed and `ShadowJar.stats` is `internal` for now. -- Polish `startShadowScripts` task registering. ([#1216](https://github.com/GradleUp/shadow/pull/1216)) - -### Fixed - -- Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) -- Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) - -### Removed - -- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) - -## [9.0.0-beta6](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta6) - 2025-01-23 - -### Added - -- Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) - -### Fixed - -- Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) - -## [9.0.0-beta5](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta5) - 2025-01-21 - -### Added - -- Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) - -### Changed - -- Exclude kotlin-stdlib from plugin dependencies. ([#1093](https://github.com/GradleUp/shadow/pull/1093)) -- **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) - Some public `List` parameters are also changed to `Set`. -- Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) -- Support configuring `separator` in `AppendingTransformer`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) - This is useful for handling files like `resources/application.yml`. - -### Fixed - -- Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) - -## [9.0.0-beta4](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta4) - 2024-12-06 - -### Changed - -- **BREAKING CHANGE:** Some public getters are removed and updated in `SimpleRelocator`. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) - -### Fixed - -- Adjust property initializations and modifiers in `ShadowJar`. ([#1090](https://github.com/GradleUp/shadow/pull/1090)) - This fixes the regression for registering custom `ShadowJar` tasks. - -## [9.0.0-beta2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta2) - 2024-11-28 - -### Fixed - -- Revert "Migrate SimpleRelocator to using lazy properties" ([#1052](https://github.com/GradleUp/shadow/pull/1052)) - This fixes the relocation not working in `v9.0.0-beta1`. - -## [9.0.0-beta1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-beta1) - 2024-11-27 - -### Added - -- Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) - -### Changed - -- **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) -- **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) -- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) - `isEnableRelocation` is removed, use `enableRelocation` instead. -- **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) -- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) - -### Removed - -- **BREAKING CHANGE:** Remove Develocity integration. ([#1014](https://github.com/GradleUp/shadow/pull/1014)) - -### Fixed - -- Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) - ## [8.3.9](https://github.com/GradleUp/shadow/releases/tag/8.3.9) - 2025-08-05 **Changed** From 7705e8adbd6bf384e305359011319bf911e757c7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 19 Sep 2025 11:41:43 +0800 Subject: [PATCH 770/941] Remove release notes for 9.0 rc versions (#1764) They are still available on GitHub releases. - https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1 - https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc2 - https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3 --- docs/changes/README.md | 167 +---------------------------------------- 1 file changed, 4 insertions(+), 163 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 6988fd582..b65692946 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -125,6 +125,10 @@ You can diff the shadowed JARs when upgrading from 8.x to 9.x by using [Diffuse](https://github.com/JakeWharton/diffuse). If there are any things missing in the changelog or the doc site, please report them to us. +!!! note + + Release notes for 9.0.0 beta and rc versions are available on GitHub Releases: https://github.com/GradleUp/shadow/releases + ### Added - Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) @@ -307,169 +311,6 @@ If you used Shadow for merging service files, the following steps are recommende 5. Diff the JARs again, and check that only the entries you want to preserve remain. 6. Optionally, if you want a stricter check for the shadowed JAR entries, enable `failOnDuplicateEntries`. -## [9.0.0-rc3](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc3) - 2025-08-01 - -### Added - -- Add `PreserveFirstFoundResourceTransformer`. ([#1548](https://github.com/GradleUp/shadow/pull/1548)) - This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and - want to ensure that only the first found resource is included in the final JAR. -- Fail build if the ZIP entries in the shadowed JAR are duplicate. ([#1552](https://github.com/GradleUp/shadow/pull/1552)) - This feature is controlled by the `shadowJar.failOnDuplicateEntries` property, which is `false` by default. - Related to setting `duplicatesStrategy = DuplicatesStrategy.FAIL` but there are some differences: - - It only checks the entries in the shadowed jar, not the input files. - - It works with setting `duplicatesStrategy` to any value. - - It provides a stricter check before the JAR is created. - -### Changed - -- **BREAKING CHANGE:** Rename `ShadowJar`'s `enableRelocation` to `enableAutoRelocation`. ([#1541](https://github.com/GradleUp/shadow/pull/1541)) - The Command Line options are also updated: - ``` - --enable-auto-relocation Enables auto relocation of packages in the dependencies. - --no-enable-auto-relocation Disables option --enable-auto-relocation. - --fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate. - --no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries. - --minimize-jar Minimizes the jar by removing unused classes. - --no-minimize-jar Disables option --minimize-jar. - --relocation-prefix Prefix used for auto relocation of packages in the dependencies. - --rerun Causes the task to be re-run even if up-to-date. - ``` -- Mark `Action` parameters as non-null. ([#1555](https://github.com/GradleUp/shadow/pull/1555)) - -### Removed - -- Remove JVM default compat stuff. ([#1556](https://github.com/GradleUp/shadow/pull/1556)) - -## [9.0.0-rc2](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc2) - 2025-07-23 - -### Added - -- Support skipping string constant remapping. ([#1401](https://github.com/GradleUp/shadow/pull/1401)) -- Let `assemble` depend on `shadowJar`. ([#1524](https://github.com/GradleUp/shadow/pull/1524)) -- Fail build when inputting AAR files or using Shadow with AGP. ([#1530](https://github.com/GradleUp/shadow/pull/1530)) - -### Changed - -- Restore Develocity Build Scan integration. ([#1505](https://github.com/GradleUp/shadow/pull/1505)) - It is still disabled by default, you can enable it by setting `com.gradleup.shadow.enableDevelocityIntegration = true`. -- Expose `AbstractDependencyFilter` from `internal` to `public`. ([#1538](https://github.com/GradleUp/shadow/pull/1538)) - You can access it via `com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter.AbstractDependencyFilter`. - -### Fixed - -- Honor `options.release` for target JVM attribute. ([#1502](https://github.com/GradleUp/shadow/pull/1502)) - -## [9.0.0-rc1](https://github.com/GradleUp/shadow/releases/tag/9.0.0-rc1) - 2025-07-02 - -!!! note - - Release notes for 9.0.0 beta versions are available on GitHub Releases: https://github.com/GradleUp/shadow/releases - -### Added - -- Add .md support to the Apache License and Notice transformers. ([#1041](https://github.com/GradleUp/shadow/pull/1041)) -- Sync `SimpleRelocator` changes from maven-shade-plugin. ([#1076](https://github.com/GradleUp/shadow/pull/1076)) -- Support configuring `separator` in `AppendingTransformer`. ([#1169](https://github.com/GradleUp/shadow/pull/1169)) - This is useful for handling files like `resources/application.yml`. -- Exclude `module-info.class` in Multi-Release folders by default. ([#1177](https://github.com/GradleUp/shadow/pull/1177)) -- Inject `TargetJvmVersion` attribute for Gradle Module Metadata. ([#1199](https://github.com/GradleUp/shadow/pull/1199)) -- Sync `ShadowApplicationPlugin` with `ApplicationPlugin`. ([#1224](https://github.com/GradleUp/shadow/pull/1224)) -- Inject `Multi-Release` manifest attribute if any dependency contains it. ([#1239](https://github.com/GradleUp/shadow/pull/1239)) -- Mark `Transformer` as throwing `IOException`. ([#1248](https://github.com/GradleUp/shadow/pull/1248)) -- Compat Kotlin Multiplatform plugin. ([#1280](https://github.com/GradleUp/shadow/pull/1280)) -- 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)) -- Support command line options for `ShadowJar`. ([#1365](https://github.com/GradleUp/shadow/pull/1365)) - ``` - --enable-relocation Enable relocation of packages in the jar - --no-enable-relocation Disables option --enable-relocation - --minimize-jar Minimize the jar by removing unused classes - --no-minimize-jar Disables option --minimize-jar - --relocation-prefix Prefix to use for relocated packages - --rerun Causes the task to be re-run even if up-to-date - ``` - -### Changed - -- **BREAKING CHANGE:** Rewrite this plugin in Kotlin. ([#1012](https://github.com/GradleUp/shadow/pull/1012)) -- **BREAKING CHANGE:** Migrate `Transformer`s to using lazy properties. ([#1036](https://github.com/GradleUp/shadow/pull/1036)) -- **BREAKING CHANGE:** Migrate `ShadowJar` to using lazy properties. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) -- **BREAKING CHANGE:** `ShadowJar`'s `isEnableRelocation` has been renamed to `enableRelocation`. ([#1044](https://github.com/GradleUp/shadow/pull/1044)) -- **BREAKING CHANGE:** Resolve `Configuration` directly in `DependencyFilter`. ([#1045](https://github.com/GradleUp/shadow/pull/1045)) -- **BREAKING CHANGE:** Migrate `SimpleRelocator` to using lazy properties. ([#1047](https://github.com/GradleUp/shadow/pull/1047)) -- **BREAKING CHANGE:** Some public getters have been updated in `SimpleRelocator`. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) -- **BREAKING CHANGE:** Migrate all `ListProperty` usages to `SetProperty`. ([#1103](https://github.com/GradleUp/shadow/pull/1103)) - Some public `List` parameters are also changed to `Set`. -- **BREAKING CHANGE:** Mark `RelocatorRemapper` as `internal`. ([#1227](https://github.com/GradleUp/shadow/pull/1227)) -- **BREAKING CHANGE:** Bump min Java requirement to 11. ([#1242](https://github.com/GradleUp/shadow/pull/1242)) -- **BREAKING CHANGE:** Move tracking unused classes logic out of `ShadowCopyAction`. ([#1257](https://github.com/GradleUp/shadow/pull/1257)) -- Reduce duplicate `SimpleRelocator` to improve performance. ([#1271](https://github.com/GradleUp/shadow/pull/1271)) -- **BREAKING CHANGE:** Move `DependencyFilter` into `tasks` package. ([#1272](https://github.com/GradleUp/shadow/pull/1272)) -- **BREAKING CHANGE:** Change the default `duplicatesStrategy` from `EXCLUDE` to `INCLUDE`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - - `ShadowJar` recognized `DuplicatesStrategy.EXCLUDE` as the default, but the other strategies didn't work properly. - - Now `ShadowJar` honors `DuplicatesStrategy.INCLUDE` as the default, and align all the strategy behaviors with the Gradle side. -- **BREAKING CHANGE:** Align the behavior of `ShadowTask.from` with Gradle's `AbstractCopyTask.from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) - In the previous versions, `ShadowTask.from` would always unzip the files before processing them, which caused serial - issues that are hard to fix. Now it behaves like Gradle's `AbstractCopyTask.from`, which means it will not unzip - the files, only copy the files as-is. If you still want to shadow the unzipped files, try out something like: - ```kotlin - tasks.shadowJar { - from(zipTree(files('path/to/your/file.zip'))) - } - ``` - or - ```kotlin - dependencies { - implementation(files('path/to/your/file.zip')) - } - ``` -- **BREAKING CHANGE:** Rename `Transformer` to `ResourceTransformer`. ([#1288](https://github.com/GradleUp/shadow/pull/1288)) - Aims to better align with the name `org.apache.maven.plugins.shade.resource.ResourceTransformer.java` - and to distinguish itself from `org.gradle.api.Transformer.java`. -- **BREAKING CHANGE:** Mark `DefaultInheritManifest` as `internal`. ([#1303](https://github.com/GradleUp/shadow/pull/1303)) -- **BREAKING CHANGE:** Polish `ShadowSpec`. ([#1307](https://github.com/GradleUp/shadow/pull/1307)) - - Return values of `ShadowSpec` functions are changed to `Unit` to avoid confusion. - - `ShadowSpec` no longer extends `CopySpec`. - - Overload `relocate`, `transform` and things for better usability in Kotlin. -- **BREAKING CHANGE:** Remove redundant types from function returning. ([#1308](https://github.com/GradleUp/shadow/pull/1308)) -- Replace deprecated `SelfResolvingDependency` with `FileCollectionDependency`. ([#1114](https://github.com/GradleUp/shadow/pull/1114)) -- Update start script templates. ([#1183](https://github.com/GradleUp/shadow/pull/1183)) -- Mark more `Transformer`s cacheable. ([#1210](https://github.com/GradleUp/shadow/pull/1210)) -- Mark `ShadowJar.dependencyFilter` as `@Input`. ([#1206](https://github.com/GradleUp/shadow/pull/1206)) -- Polish `startShadowScripts` task registering. ([#1216](https://github.com/GradleUp/shadow/pull/1216)) -- Refactor file visiting logic in `StreamAction`, handle file unzipping via `Project.zipTree`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -- Migrate doc sites to MkDocs. ([#1302](https://github.com/GradleUp/shadow/pull/1302)) -- `runShadow` no longer depends on `installShadowDist`. ([#1353](https://github.com/GradleUp/shadow/pull/1353)) -- Move the group of `ShadowJar` from `shadow` to `build`. ([#1355](https://github.com/GradleUp/shadow/pull/1355)) -- In-development snapshots are now published to the Central Portal Snapshots repository. ([#1414](https://github.com/GradleUp/shadow/pull/1414)) - -### Fixed - -- Fix single Log4j2Plugins.dat isn't included into fat jar. ([#1039](https://github.com/GradleUp/shadow/issues/1039)) -- Fail builds if processing bad jars. ([#1146](https://github.com/GradleUp/shadow/pull/1146)) -- Fix `Log4j2PluginsCacheFileTransformer` not working for merging `Log4j2Plugins.dat` files. ([#1175](https://github.com/GradleUp/shadow/pull/1175)) -- Support overriding `mainClass` provided by `JavaApplication`. ([#1182](https://github.com/GradleUp/shadow/pull/1182)) -- Fix `ShadowJar` not being successful after `includes` or `excludes` are changed. ([#1200](https://github.com/GradleUp/shadow/pull/1200)) -- Honor `DuplicatesStrategy`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -- Honor unzipped jars via `from`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -- Fix the last modified time of shadowed directories. ([#1277](https://github.com/GradleUp/shadow/pull/1277)) -- Fix relocation exclusion for file patterns like `kotlin/kotlin.kotlin_builtins`. ([#1313](https://github.com/GradleUp/shadow/pull/1313)) -- Allow using file trees of JARs together with the configuration cache. ([#1441](https://github.com/GradleUp/shadow/pull/1441)) - -### Removed - -- **BREAKING CHANGE:** Remove Develocity integration. ([#1014](https://github.com/GradleUp/shadow/pull/1014)) -- **BREAKING CHANGE:** Some public getters and setters have been removed in `SimpleRelocator`. ([#1079](https://github.com/GradleUp/shadow/pull/1079)) -- **BREAKING CHANGE:** Remove `JavaJarExec`, now use `JavaExec` directly for `runShadow` task. ([#1197](https://github.com/GradleUp/shadow/pull/1197)) -- **BREAKING CHANGE:** `ServiceFileTransformer.ServiceStream` has been removed. ([#1218](https://github.com/GradleUp/shadow/pull/1218)) -- **BREAKING CHANGE:** Remove `KnowsTask` as it's useless. ([#1236](https://github.com/GradleUp/shadow/pull/1236)) -- **BREAKING CHANGE:** Remove `BaseStreamAction`. ([#1258](https://github.com/GradleUp/shadow/pull/1258)) -- **BREAKING CHANGE:** Remove `ShadowStats`. ([#1264](https://github.com/GradleUp/shadow/pull/1264)) -- **BREAKING CHANGE:** Remove `ShadowCopyAction.ArchiveFileTreeElement` and `RelativeArchivePath`. ([#1233](https://github.com/GradleUp/shadow/pull/1233)) -- **BREAKING CHANGE:** Remove `TransformerContext.getEntryTimestamp`. ([#1245](https://github.com/GradleUp/shadow/pull/1245)) -- **BREAKING CHANGE:** Reduce dependency and project overloads in `DependencyFilter`. ([#1328](https://github.com/GradleUp/shadow/pull/1328)) - ## [8.3.9](https://github.com/GradleUp/shadow/releases/tag/8.3.9) - 2025-08-05 **Changed** From ff5d9e2a54c685a3f5c8b4aa67bc9afdfc3c9389 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 19 Sep 2025 11:57:25 +0800 Subject: [PATCH 771/941] Link format --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index b65692946..593e23ae1 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -127,7 +127,7 @@ !!! note - Release notes for 9.0.0 beta and rc versions are available on GitHub Releases: https://github.com/GradleUp/shadow/releases + Release notes for 9.0.0 beta and rc versions are available on [GitHub Releases](https://github.com/GradleUp/shadow/releases). ### Added From 411c4ac2865a5d5bf88ac35e4c1958d444c0da3a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 19 Sep 2025 11:59:20 +0800 Subject: [PATCH 772/941] Tweak the migration example in 9.0.0 notes (#1765) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 14 ++++++-------- docs/configuration/merging/README.md | 1 + 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 593e23ae1..efc59a7c6 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -283,15 +283,10 @@ tasks.shadowJar { // `isEnableRelocation` has been renamed to `enableAutoRelocation`. enableAutoRelocation = true - // If you want to make `mergeServiceFiles` work, should set the `duplicatesStrategy` as `INCLUDE`. - // `EXCLUDE` will exclude extra service files to be merged. + // If you want to make `mergeServiceFiles` or most resource transformers work, you should set the `duplicatesStrategy` to `INCLUDE`. + // Because `EXCLUDE` will exclude extra service files to be merged. duplicatesStrategy = DuplicatesStrategy.INCLUDE mergeServiceFiles() - // If you leave `duplicatesStrategy` as `INCLUDE`, you can use the new `PreserveFirstFoundResourceTransformer` - // to ensure that only the first found resource is included in the final JAR. Or duplicate entries will be bundled. - transform() { - resources.add("META-INF/some-resource.txt") - } // Optionally, you can enable the new `failOnDuplicateEntries` property to fail the build if there are duplicate entries. failOnDuplicateEntries = true @@ -307,9 +302,12 @@ If you used Shadow for merging service files, the following steps are recommende 1. Make sure to leave `duplicatesStrategy` as `INCLUDE` or `WARN`. 2. Apply `mergeServiceFiles` or `ServiceFileTransformer` stuff as you did in your previous setup. 3. Diff the JARs from upgrading or not. -4. Remove the extra entries that are added by `INCLUDE` by `filesMatching` or `PreserveFirstFoundResourceTransformer`. +4. Remove the extra entries that are added by `INCLUDE` by `eachFile`, `filesMatching`, or `PreserveFirstFoundResourceTransformer`. 5. Diff the JARs again, and check that only the entries you want to preserve remain. 6. Optionally, if you want a stricter check for the shadowed JAR entries, enable `failOnDuplicateEntries`. + This can also ensure the regressions are caught in the future. + +See more details about the fixed `DuplicatesStrategy` behaviors at [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy). ## [8.3.9](https://github.com/GradleUp/shadow/releases/tag/8.3.9) - 2025-08-05 diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index f332acd8a..0eea6f3f8 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -103,6 +103,7 @@ Alternatively, you can follow these steps: Optional steps: - Enable [`ShadowJar.failOnDuplicateEntries`][ShadowJar.failOnDuplicateEntries] to check duplicate entries in the final JAR. + This can also ensure the regressions are caught in the future. - Use [Diffuse](https://github.com/JakeWharton/diffuse) to diff the JARs. Here are some examples: From 9e943d01cd996c61219cd2221b2549f161569c8f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 19 Sep 2025 12:09:27 +0800 Subject: [PATCH 773/941] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..44c56beb1 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: Goooler From ecb1f488d9cb49d7da50f1c32112b809231f091f Mon Sep 17 00:00:00 2001 From: Jonathing Date: Sat, 20 Sep 2025 05:03:01 -0400 Subject: [PATCH 774/941] Make assemble depend on shadowJar even if it is added later (#1766) Currently, Shadow makes the assemble task depend on shadowJar if and only if it exists when the shadow plugin is applied. This is usually fine, but in the case of some unorthodox setups, it can mean that if the Shadow plugin is applied before the base plugin, the assemble task can end up existing without depending on the shadowJar task. To address this, instead of using `TaskContainer#findByName(String)`, we can use `#named(Spec)` to work with a live view of the container. This is very similar to how `#withType(Class` works, where `configureEach` applies the action to each member that passes the spec, even if it is added later. --------- Co-authored-by: Goooler --- docs/changes/README.md | 1 + .../jengelman/gradle/plugins/shadow/JavaPluginsTest.kt | 6 +++++- .../jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 9 ++++++--- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index efc59a7c6..bfa64bb9c 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -31,6 +31,7 @@ - Stop using start script templates bundled in Shadow. ([#1738](https://github.com/GradleUp/shadow/pull/1738)) - Bump min Java requirement to 17. ([#1744](https://github.com/GradleUp/shadow/pull/1744)) - Require most optional properties non-null. ([#1745](https://github.com/GradleUp/shadow/pull/1745)) +- Make assemble depend on shadowJar even if it is added later. ([#1766](https://github.com/GradleUp/shadow/pull/1766)) ### Fixed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 90cea057d..9da7bef5f 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -37,6 +37,7 @@ import kotlin.io.path.name import kotlin.io.path.outputStream import kotlin.io.path.readText import kotlin.io.path.writeText +import org.gradle.api.Named import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.internal.tasks.JvmConstants import org.gradle.api.plugins.JavaPlugin @@ -77,7 +78,10 @@ class JavaPluginsTest : BasePluginTest() { val shadowTask = project.tasks.getByName(SHADOW_JAR_TASK_NAME) as ShadowJar val shadowConfig = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) val assembleTask = project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME) - assertThat(assembleTask.dependsOn).contains(shadowTask) + assertThat(assembleTask.dependsOn.filterIsInstance().map { it.name }).all { + isNotEmpty() + contains(shadowTask.name) + } // Check inherited properties. with(shadowTask as Jar) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index f988a1d1f..3a84fe978 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -537,10 +537,13 @@ public abstract class ShadowJar : Jar() { jarTask.get().manifest, ) - @Suppress("EagerGradleConfiguration") // Can't use `named` as the task is optional. - tasks.findByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME)?.dependsOn(task) - action(task) + }.also { task -> + // Can't use `named` directly as the task is optional or may not exist when the plugin is applied. + // Using Spec applies the action to the task if it is added later. + tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME::equals).configureEach { + it.dependsOn(task) + } } } } From d6515d622661b59b3bb8ca08b1f5026dca9736d2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 20 Sep 2025 19:41:18 +0800 Subject: [PATCH 775/941] Test deferred assemble task dependencies (#1768) --- .../gradle/plugins/shadow/JavaPluginsTest.kt | 50 ++++++++++++++++++- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 4 +- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 9da7bef5f..bd4177f6e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -37,6 +37,8 @@ import kotlin.io.path.name import kotlin.io.path.outputStream import kotlin.io.path.readText import kotlin.io.path.writeText +import kotlin.reflect.full.declaredFunctions +import kotlin.reflect.jvm.javaMethod import org.gradle.api.Named import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.internal.tasks.JvmConstants @@ -46,7 +48,9 @@ import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.tasks.bundling.Jar import org.gradle.api.tasks.bundling.ZipEntryCompression import org.gradle.language.base.plugins.LifecycleBasePlugin +import org.gradle.language.base.plugins.LifecycleBasePlugin.ASSEMBLE_TASK_NAME import org.gradle.testfixtures.ProjectBuilder +import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -77,7 +81,7 @@ class JavaPluginsTest : BasePluginTest() { project.plugins.apply(JavaPlugin::class.java) val shadowTask = project.tasks.getByName(SHADOW_JAR_TASK_NAME) as ShadowJar val shadowConfig = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) - val assembleTask = project.tasks.getByName(LifecycleBasePlugin.ASSEMBLE_TASK_NAME) + val assembleTask = project.tasks.getByName(ASSEMBLE_TASK_NAME) assertThat(assembleTask.dependsOn.filterIsInstance().map { it.name }).all { isNotEmpty() contains(shadowTask.name) @@ -122,6 +126,50 @@ class JavaPluginsTest : BasePluginTest() { assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile) } + @Issue( + "https://github.com/GradleUp/shadow/pull/1766", + ) + @Test + fun makeAssembleDependOnShadowJarEvenIfAddedLater() { + val kFunction = ShadowJar.Companion::class.declaredFunctions + .single { it.name == "registerShadowJarCommon" } + val jvmName = requireNotNull(kFunction.javaMethod).name + + projectScript.writeText( + """ + plugins { + id('com.gradleup.shadow') + } + + def testJar = tasks.register('testJar', Jar) + // Must use `@Companion` to access the companion object instance instead of the class. + def companion = ${ShadowJar::class.qualifiedName}.@Companion + companion.$jvmName(project, testJar) { + it.archiveFile.set(project.layout.buildDirectory.file('libs/test-all.jar')) + } + + afterEvaluate { + tasks.register('$ASSEMBLE_TASK_NAME') { + def taskDependencies = provider { dependsOn.collect { it.name }.join(', ') } + doFirst { + logger.lifecycle('task dependencies: ' + taskDependencies.get()) + } + } + } + """.trimIndent(), + ) + + val result = run(ASSEMBLE_TASK_NAME) + + assertThat(result.task(":$ASSEMBLE_TASK_NAME")).isNotNull() + .transform { it.outcome }.isEqualTo(SUCCESS) + assertThat(result.task(shadowJarPath)).isNotNull() + .transform { it.outcome }.isEqualTo(SUCCESS) + assertThat(result.output).contains( + "task dependencies: $SHADOW_JAR_TASK_NAME", + ) + } + @Test fun shadowJarCliOptions() { val result = run("help", "--task", shadowJarPath) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 3a84fe978..904b039c4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -517,7 +517,7 @@ public abstract class ShadowJar : Jar() { internal fun Project.registerShadowJarCommon( jarTask: TaskProvider, - action: (ShadowJar) -> Unit, + action: Action, ): TaskProvider { return tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> task.archiveClassifier.set("all") @@ -537,7 +537,7 @@ public abstract class ShadowJar : Jar() { jarTask.get().manifest, ) - action(task) + action.execute(task) }.also { task -> // Can't use `named` directly as the task is optional or may not exist when the plugin is applied. // Using Spec applies the action to the task if it is added later. From bf7cd135fc78a18de7c7139467bad4af262bbcf8 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 20 Sep 2025 20:09:36 +0800 Subject: [PATCH 776/941] Tweak style --- .../github/jengelman/gradle/plugins/shadow/BasePluginTest.kt | 5 +++-- .../jengelman/gradle/plugins/shadow/JavaPluginsTest.kt | 2 +- 2 files changed, 4 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 40217f1d9..ec6b2eeaa 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 @@ -149,8 +149,8 @@ abstract class BasePluginTest { val versionInfo = if (withVersion) "version = '1.0'" else "" return """ plugins { - id('$plugin') - id('com.gradleup.shadow') + id '$plugin' + id '$shadowPluginId' } $groupInfo $versionInfo @@ -424,6 +424,7 @@ abstract class BasePluginTest { Path(gradleUserHome, "testkit") } + const val shadowPluginId = "com.gradleup.shadow" const val shadowJarPath = ":$SHADOW_JAR_TASK_NAME" const val serverShadowJarPath = ":server:$SHADOW_JAR_TASK_NAME" const val runShadowPath = ":$SHADOW_RUN_TASK_NAME" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index bd4177f6e..cfcf91369 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -138,7 +138,7 @@ class JavaPluginsTest : BasePluginTest() { projectScript.writeText( """ plugins { - id('com.gradleup.shadow') + id '$shadowPluginId' } def testJar = tasks.register('testJar', Jar) From 38d0a3d14a162958a248eaeaa340842e31a4092b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 20 Sep 2025 23:29:27 +0800 Subject: [PATCH 777/941] TODO usages of conventionMapping (#1770) --- .../jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index f5b05ad69..4efaa6d2d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -58,7 +58,7 @@ public abstract class ShadowApplicationPlugin : Plugin { task.classpath = files(tasks.shadowJar) - @Suppress("InternalGradleApiUsage") // Usages of conventionMapping. + @Suppress("InternalGradleApiUsage") // TODO: replace usages of conventionMapping. with(applicationExtension) { task.mainModule.convention(mainModule) task.mainClass.convention(mainClass) From 3efc02906a8f5afd9efc08947a2360ac85d89e44 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 21 Sep 2025 10:21:04 +0800 Subject: [PATCH 778/941] Group property value tests into a new class (#1772) --- .../gradle/plugins/shadow/JavaPluginsTest.kt | 82 +----------- .../plugins/shadow/ShadowPropertiesTest.kt | 119 ++++++++++++++++++ 2 files changed, 121 insertions(+), 80 deletions(-) create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index cfcf91369..b4cbc63cf 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -4,23 +4,18 @@ import assertk.all import assertk.assertThat import assertk.assertions.contains import assertk.assertions.containsMatch -import assertk.assertions.containsOnly import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo -import assertk.assertions.isFalse import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull import assertk.assertions.isNull -import assertk.assertions.isTrue import assertk.assertions.single import com.github.jengelman.gradle.plugins.shadow.ShadowPlugin.Companion.ENABLE_DEVELOCITY_INTEGRATION_PROPERTY import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey -import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration -import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast @@ -39,17 +34,11 @@ import kotlin.io.path.readText import kotlin.io.path.writeText import kotlin.reflect.full.declaredFunctions import kotlin.reflect.jvm.javaMethod -import org.gradle.api.Named -import org.gradle.api.file.DuplicatesStrategy -import org.gradle.api.internal.tasks.JvmConstants -import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME -import org.gradle.api.tasks.bundling.Jar +import org.gradle.api.plugins.JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME import org.gradle.api.tasks.bundling.ZipEntryCompression -import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.base.plugins.LifecycleBasePlugin.ASSEMBLE_TASK_NAME -import org.gradle.testfixtures.ProjectBuilder import org.gradle.testkit.runner.TaskOutcome.SUCCESS import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest @@ -59,73 +48,6 @@ import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.ValueSource class JavaPluginsTest : BasePluginTest() { - @Test - fun applyPlugin() { - val projectName = "my-shadow" - val version = "1.0.0" - - val project = ProjectBuilder.builder().withName(projectName).build().also { - it.version = version - } - project.plugins.apply(ShadowPlugin::class.java) - - assertThat(project.plugins.hasPlugin(ShadowPlugin::class.java)).isTrue() - assertThat(project.plugins.hasPlugin(LegacyShadowPlugin::class.java)).isTrue() - assertThat(project.tasks.findByName(SHADOW_JAR_TASK_NAME)).isNull() - - with(project.extensions.getByType(ShadowExtension::class.java)) { - assertThat(addShadowVariantIntoJavaComponent.get()).isTrue() - assertThat(addTargetJvmVersionAttribute.get()).isTrue() - } - - project.plugins.apply(JavaPlugin::class.java) - val shadowTask = project.tasks.getByName(SHADOW_JAR_TASK_NAME) as ShadowJar - val shadowConfig = project.configurations.getByName(ShadowBasePlugin.CONFIGURATION_NAME) - val assembleTask = project.tasks.getByName(ASSEMBLE_TASK_NAME) - assertThat(assembleTask.dependsOn.filterIsInstance().map { it.name }).all { - isNotEmpty() - contains(shadowTask.name) - } - - // Check inherited properties. - with(shadowTask as Jar) { - assertThat(group).isEqualTo(LifecycleBasePlugin.BUILD_GROUP) - assertThat(description).isEqualTo("Create a combined JAR of project and runtime dependencies") - - assertThat(archiveAppendix.orNull).isNull() - assertThat(archiveBaseName.get()).isEqualTo(projectName) - assertThat(archiveClassifier.get()).isEqualTo("all") - assertThat(archiveExtension.get()).isEqualTo("jar") - assertThat(archiveFileName.get()).isEqualTo("my-shadow-1.0.0-all.jar") - assertThat(archiveVersion.get()).isEqualTo(version) - assertThat(archiveFile.get().asFile).all { - isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) - isEqualTo(project.projectDir.resolve("build/libs/my-shadow-1.0.0-all.jar")) - } - assertThat(destinationDirectory.get().asFile) - .isEqualTo(project.layout.buildDirectory.dir("libs").get().asFile) - - assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.EXCLUDE) - } - - // Check self properties. - with(shadowTask) { - assertThat(addMultiReleaseAttribute.get()).isTrue() - assertThat(enableAutoRelocation.get()).isFalse() - assertThat(failOnDuplicateEntries.get()).isFalse() - assertThat(minimizeJar.get()).isFalse() - assertThat(mainClass.orNull).isNull() - - assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) - assertThat(configurations.get()).all { - isNotEmpty() - containsOnly(project.runtimeConfiguration) - } - } - - assertThat(shadowConfig.artifacts.files).contains(shadowTask.archiveFile.get().asFile) - } - @Issue( "https://github.com/GradleUp/shadow/pull/1766", ) @@ -497,7 +419,7 @@ class JavaPluginsTest : BasePluginTest() { "https://github.com/GradleUp/shadow/issues/65", ) @ParameterizedTest - @ValueSource(strings = [ShadowBasePlugin.CONFIGURATION_NAME, JvmConstants.IMPLEMENTATION_CONFIGURATION_NAME]) + @ValueSource(strings = [ShadowBasePlugin.CONFIGURATION_NAME, IMPLEMENTATION_CONFIGURATION_NAME]) fun addShadowConfigurationToClassPathInManifest(configuration: String) { projectScript.appendText( """ diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt new file mode 100644 index 000000000..59c0a3657 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -0,0 +1,119 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.containsNone +import assertk.assertions.containsOnly +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isNotEmpty +import assertk.assertions.isNull +import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow +import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration +import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shadowJar +import org.gradle.api.Named +import org.gradle.api.Project +import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME +import org.gradle.api.tasks.bundling.Jar +import org.gradle.language.base.plugins.LifecycleBasePlugin +import org.gradle.language.base.plugins.LifecycleBasePlugin.ASSEMBLE_TASK_NAME +import org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Test + +class ShadowPropertiesTest { + private lateinit var project: Project + + @BeforeEach + fun beforeEach() { + project = ProjectBuilder.builder().withName(PROJECT_NAME).build().also { + it.version = VERSION + it.plugins.apply(ShadowPlugin::class.java) + } + } + + @Test + fun misc() = with(project) { + assertThat(plugins.hasPlugin(ShadowPlugin::class.java)).isTrue() + assertThat(plugins.hasPlugin(LegacyShadowPlugin::class.java)).isTrue() + assertThat(tasks.findByName(SHADOW_JAR_TASK_NAME)).isNull() + + with(extensions.getByType(ShadowExtension::class.java)) { + assertThat(addShadowVariantIntoJavaComponent.get()).isTrue() + assertThat(addTargetJvmVersionAttribute.get()).isTrue() + } + } + + @Test + fun applyJavaPlugin() = with(project) { + plugins.apply(JavaPlugin::class.java) + val shadowJarTask = tasks.shadowJar.get() + val shadowConfig = configurations.shadow.get() + val assembleTask = tasks.getByName(ASSEMBLE_TASK_NAME) + + assertThat(shadowConfig.artifacts.files).contains(shadowJarTask.archiveFile.get().asFile) + assertThat(assembleTask.dependsOn.filterIsInstance().map { it.name }).all { + isNotEmpty() + contains(shadowJarTask.name) + } + + // Check inherited properties. + with(shadowJarTask as Jar) { + assertThat(group).isEqualTo(LifecycleBasePlugin.BUILD_GROUP) + assertThat(description).isEqualTo("Create a combined JAR of project and runtime dependencies") + + assertThat(archiveAppendix.orNull).isNull() + assertThat(archiveBaseName.get()).isEqualTo(PROJECT_NAME) + assertThat(archiveClassifier.get()).isEqualTo("all") + assertThat(archiveExtension.get()).isEqualTo("jar") + assertThat(archiveFileName.get()).isEqualTo("my-shadow-1.0.0-all.jar") + assertThat(archiveVersion.get()).isEqualTo(version) + assertThat(archiveFile.get().asFile).all { + isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) + isEqualTo(projectDir.resolve("build/libs/my-shadow-1.0.0-all.jar")) + } + assertThat(destinationDirectory.get().asFile) + .isEqualTo(layout.buildDirectory.dir("libs").get().asFile) + + assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.EXCLUDE) + } + + // Check self properties. + with(shadowJarTask) { + assertThat(addMultiReleaseAttribute.get()).isTrue() + assertThat(enableAutoRelocation.get()).isFalse() + assertThat(failOnDuplicateEntries.get()).isFalse() + assertThat(minimizeJar.get()).isFalse() + assertThat(mainClass.orNull).isNull() + + assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) + assertThat(configurations.get()).all { + isNotEmpty() + containsOnly(runtimeConfiguration) + } + } + } + + @Test + fun applyJavaGradlePlugin() = with(project) { + plugins.apply(JavaGradlePluginPlugin::class.java) + val api = configurations.named(API_CONFIGURATION_NAME).get() + val compileOnly = configurations.named(COMPILE_ONLY_CONFIGURATION_NAME).get() + val gradleApi = dependencies.gradleApi() + assertThat(api.dependencies).containsNone(gradleApi) + assertThat(compileOnly.dependencies).contains(gradleApi) + } + + private companion object { + const val PROJECT_NAME = "my-shadow" + const val VERSION = "1.0.0" + } +} From 90cfaa6c497d63fdeb56f57f74f4f95e355662ef Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 21 Sep 2025 15:30:41 +0800 Subject: [PATCH 779/941] Add task accessors and check more task properties (#1771) * Check `runShadowTask` properties * Check `startShadowScripts` properties * Add accessor for `runShadow` * Add accessors for `shadowDistTar` and `shadowDistZip` * Update changelog * New `DistributionContainer.shadow` * Test `applyApplicationPlugin` * Rename `PROJECT_NAME` * Cleanups * Check `shadowDistTar` * Use `distributions.shadow` in doc * Show accessors in docs * Use `containsOnly` --- api/shadow.api | 6 + docs/application-plugin/README.md | 28 +++- docs/changes/README.md | 1 + .../plugins/shadow/ShadowApplicationPlugin.kt | 23 ++++ .../plugins/shadow/ShadowPropertiesTest.kt | 122 +++++++++++++++--- 5 files changed, 163 insertions(+), 17 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 2a33b3432..f48941658 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -1,6 +1,8 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin : org/gradle/api/Plugin { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion; public static final field DISTRIBUTION_NAME Ljava/lang/String; + public static final field SHADOW_DIST_TAR_TASK_NAME Ljava/lang/String; + public static final field SHADOW_DIST_ZIP_TASK_NAME Ljava/lang/String; public static final field SHADOW_INSTALL_TASK_NAME Ljava/lang/String; public static final field SHADOW_RUN_TASK_NAME Ljava/lang/String; public static final field SHADOW_SCRIPTS_TASK_NAME Ljava/lang/String; @@ -16,6 +18,10 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowApplicati public final class com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin$Companion { public final synthetic fun getInstallShadowDist (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; + public final synthetic fun getRunShadow (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; + public final synthetic fun getShadow (Lorg/gradle/api/distribution/DistributionContainer;)Lorg/gradle/api/NamedDomainObjectProvider; + public final synthetic fun getShadowDistTar (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; + public final synthetic fun getShadowDistZip (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; public final synthetic fun getStartShadowScripts (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; } diff --git a/docs/application-plugin/README.md b/docs/application-plugin/README.md index 1058fd132..6d7fabf85 100644 --- a/docs/application-plugin/README.md +++ b/docs/application-plugin/README.md @@ -107,12 +107,25 @@ You can also add more files into the distribution like: } // `shadow` is the name of the distribution created by Shadow plugin - distributions.named("shadow") { + distributions.shadow { // Optionally, you can add more files into extra directory in the distribution like this: contents.from("extra/echo.sh") { into("extra") } } + + tasks.installShadowDist { + // Configure the install task if needed. + } + tasks.startShadowScripts { + // Configure the start scripts task if needed. + } + tasks.shadowDistZip { + // Configure the zip distribution task if needed. + } + tasks.shadowDistTar { + // Configure the tar distribution task if needed. + } ``` === "Groovy" @@ -138,6 +151,19 @@ You can also add more files into the distribution like: into 'extra' } } + + tasks.named('installShadowDist', Sync) { + // Configure the install task if needed. + } + tasks.named('startShadowScripts', CreateStartScripts) { + // Configure the start scripts task if needed. + } + tasks.named('shadowDistZip', Zip) { + // Configure the zip distribution task if needed. + } + tasks.named('shadowDistTar', Tar) { + // Configure the tar distribution task if needed. + } ``` View [The Distribution Plugin](https://docs.gradle.org/current/userguide/distribution_plugin.html#distribution_plugin) diff --git a/docs/changes/README.md b/docs/changes/README.md index bfa64bb9c..a3dff6dd0 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -17,6 +17,7 @@ ``` - Honor `executableDir` and `applicationName` in `application` extension. ([#1740](https://github.com/GradleUp/shadow/pull/1738)) This is useful when you want to customize the output directory of the start scripts and the application distribution. +- Provide more task accessors in `ShadowApplicationPlugin.Companion`. ([#1771](https://github.com/GradleUp/shadow/pull/1771)) ### Changed diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 4efaa6d2d..f68ecb879 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -9,14 +9,19 @@ import com.github.jengelman.gradle.plugins.shadow.internal.javaToolchainService import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shadowJar import java.io.IOException import org.gradle.api.GradleException +import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.api.distribution.Distribution +import org.gradle.api.distribution.DistributionContainer import org.gradle.api.plugins.ApplicationPlugin import org.gradle.api.tasks.JavaExec import org.gradle.api.tasks.Sync import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.application.CreateStartScripts +import org.gradle.api.tasks.bundling.Tar +import org.gradle.api.tasks.bundling.Zip /** * A [Plugin] which packages and runs a project as a Java Application using the shadowed jar. @@ -138,6 +143,16 @@ public abstract class ShadowApplicationPlugin : Plugin { public const val SHADOW_RUN_TASK_NAME: String = "runShadow" public const val SHADOW_SCRIPTS_TASK_NAME: String = "startShadowScripts" public const val SHADOW_INSTALL_TASK_NAME: String = "installShadowDist" + public const val SHADOW_DIST_TAR_TASK_NAME: String = "shadowDistTar" + public const val SHADOW_DIST_ZIP_TASK_NAME: String = "shadowDistZip" + + @get:JvmSynthetic + public inline val DistributionContainer.shadow: NamedDomainObjectProvider + get() = named(DISTRIBUTION_NAME) + + @get:JvmSynthetic + public inline val TaskContainer.runShadow: TaskProvider + get() = named(SHADOW_RUN_TASK_NAME, JavaExec::class.java) @get:JvmSynthetic public inline val TaskContainer.startShadowScripts: TaskProvider @@ -146,5 +161,13 @@ public abstract class ShadowApplicationPlugin : Plugin { @get:JvmSynthetic public inline val TaskContainer.installShadowDist: TaskProvider get() = named(SHADOW_INSTALL_TASK_NAME, Sync::class.java) + + @get:JvmSynthetic + public inline val TaskContainer.shadowDistTar: TaskProvider + get() = named(SHADOW_DIST_TAR_TASK_NAME, Tar::class.java) + + @get:JvmSynthetic + public inline val TaskContainer.shadowDistZip: TaskProvider + get() = named(SHADOW_DIST_ZIP_TASK_NAME, Zip::class.java) } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index 59c0a3657..b407fc810 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -2,25 +2,35 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.all import assertk.assertThat -import assertk.assertions.contains import assertk.assertions.containsNone import assertk.assertions.containsOnly import assertk.assertions.isEqualTo import assertk.assertions.isFalse -import assertk.assertions.isNotEmpty +import assertk.assertions.isNotNull import assertk.assertions.isNull import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.installShadowDist +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.runShadow +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.shadowDistTar +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.shadowDistZip +import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.startShadowScripts import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow +import com.github.jengelman.gradle.plugins.shadow.internal.applicationExtension +import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension +import com.github.jengelman.gradle.plugins.shadow.internal.javaToolchainService import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shadowJar import org.gradle.api.Named import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.file.DuplicatesStrategy +import org.gradle.api.plugins.ApplicationPlugin import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME +import org.gradle.api.tasks.bundling.AbstractArchiveTask import org.gradle.api.tasks.bundling.Jar import org.gradle.language.base.plugins.LifecycleBasePlugin import org.gradle.language.base.plugins.LifecycleBasePlugin.ASSEMBLE_TASK_NAME @@ -59,11 +69,8 @@ class ShadowPropertiesTest { val shadowConfig = configurations.shadow.get() val assembleTask = tasks.getByName(ASSEMBLE_TASK_NAME) - assertThat(shadowConfig.artifacts.files).contains(shadowJarTask.archiveFile.get().asFile) - assertThat(assembleTask.dependsOn.filterIsInstance().map { it.name }).all { - isNotEmpty() - contains(shadowJarTask.name) - } + assertThat(shadowConfig.artifacts.files).containsOnly(shadowJarTask.archiveFile.get().asFile) + assertThat(assembleTask.dependsOnTaskNames).containsOnly(shadowJarTask.name) // Check inherited properties. with(shadowJarTask as Jar) { @@ -74,14 +81,16 @@ class ShadowPropertiesTest { assertThat(archiveBaseName.get()).isEqualTo(PROJECT_NAME) assertThat(archiveClassifier.get()).isEqualTo("all") assertThat(archiveExtension.get()).isEqualTo("jar") - assertThat(archiveFileName.get()).isEqualTo("my-shadow-1.0.0-all.jar") + assertThat(archiveFileName.get()).isEqualTo("my-project-1.0.0-all.jar") assertThat(archiveVersion.get()).isEqualTo(version) assertThat(archiveFile.get().asFile).all { isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) - isEqualTo(projectDir.resolve("build/libs/my-shadow-1.0.0-all.jar")) + isEqualTo(projectDir.resolve("build/libs/my-project-1.0.0-all.jar")) + } + assertThat(destinationDirectory.get().asFile).all { + isEqualTo(layout.buildDirectory.dir("libs").get().asFile) + isEqualTo(projectDir.resolve("build/libs")) } - assertThat(destinationDirectory.get().asFile) - .isEqualTo(layout.buildDirectory.dir("libs").get().asFile) assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.EXCLUDE) } @@ -95,9 +104,88 @@ class ShadowPropertiesTest { assertThat(mainClass.orNull).isNull() assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) - assertThat(configurations.get()).all { - isNotEmpty() - containsOnly(runtimeConfiguration) + assertThat(configurations.get()).containsOnly(runtimeConfiguration) + } + } + + @Test + fun applyApplicationPlugin() = with(project) { + plugins.apply(ApplicationPlugin::class.java) + val shadowJarTask = tasks.shadowJar.get() + val runShadowTask = tasks.runShadow.get() + val startShadowScripts = tasks.startShadowScripts.get() + val installShadowDist = tasks.installShadowDist.get() + val shadowDistZip = tasks.shadowDistZip.get() + val shadowDistTar = tasks.shadowDistTar.get() + + with(runShadowTask) { + assertThat(description).isEqualTo("Runs this project as a JVM application using the shadow jar") + assertThat(group).isEqualTo(ApplicationPlugin.APPLICATION_GROUP) + assertThat(classpath.files).containsOnly(shadowJarTask.archiveFile.get().asFile) + assertThat(mainModule.orNull).isEqualTo(applicationExtension.mainModule.orNull) + assertThat(mainClass.orNull).isEqualTo(applicationExtension.mainClass.orNull) + assertThat(jvmArguments.get()).isEqualTo(applicationExtension.applicationDefaultJvmArgs) + assertThat(modularity.inferModulePath.orNull) + .isEqualTo(javaPluginExtension.modularity.inferModulePath.orNull) + assertThat(javaLauncher.get().metadata.jvmVersion) + .isEqualTo(javaToolchainService.launcherFor(javaPluginExtension.toolchain).get().metadata.jvmVersion) + } + + with(startShadowScripts) { + assertThat(description).isEqualTo("Creates OS specific scripts to run the project as a JVM application using the shadow jar") + assertThat(group).isEqualTo(ApplicationPlugin.APPLICATION_GROUP) + assertThat(classpath?.files).isNotNull().containsOnly(shadowJarTask.archiveFile.get().asFile) + assertThat(mainModule.orNull).isEqualTo(applicationExtension.mainModule.orNull) + assertThat(mainClass.orNull).isEqualTo(applicationExtension.mainClass.orNull) + assertThat(applicationName).isEqualTo(applicationExtension.applicationName) + assertThat(outputDir).isNotNull().all { + isEqualTo(layout.buildDirectory.dir("scriptsShadow").get().asFile) + isEqualTo(projectDir.resolve("build/scriptsShadow")) + } + assertThat(executableDir).isEqualTo(applicationExtension.executableDir) + assertThat(defaultJvmOpts).isEqualTo(applicationExtension.applicationDefaultJvmArgs) + assertThat(modularity.inferModulePath.orNull) + .isEqualTo(javaPluginExtension.modularity.inferModulePath.orNull) + } + + with(installShadowDist) { + assertThat(description).isEqualTo("Installs the project as a distribution as-is.") + assertThat(group).isEqualTo("distribution") + assertThat(destinationDir).isNotNull() + .isEqualTo(projectDir.resolve("build/install/my-project-shadow")) + } + + listOf( + shadowDistZip, + shadowDistTar, + ).forEach { + with(it as AbstractArchiveTask) { + assertThat(description).isEqualTo("Bundles the project as a distribution.") + assertThat(group).isEqualTo("distribution") + assertThat(archiveAppendix.orNull).isNull() + assertThat(archiveBaseName.get()).isEqualTo("my-project-shadow") + assertThat(archiveClassifier.orNull).isNull() + assertThat(archiveVersion.get()).isEqualTo(version) + assertThat(destinationDirectory.get().asFile).all { + isEqualTo(layout.buildDirectory.dir("distributions").get().asFile) + isEqualTo(projectDir.resolve("build/distributions")) + } + } + } + with(shadowDistZip) { + assertThat(archiveExtension.get()).isEqualTo("zip") + assertThat(archiveFileName.get()).isEqualTo("my-project-shadow-1.0.0.zip") + assertThat(archiveFile.get().asFile).all { + isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) + isEqualTo(projectDir.resolve("build/distributions/my-project-shadow-1.0.0.zip")) + } + } + with(shadowDistTar) { + assertThat(archiveExtension.get()).isEqualTo("tar") + assertThat(archiveFileName.get()).isEqualTo("my-project-shadow-1.0.0.tar") + assertThat(archiveFile.get().asFile).all { + isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) + isEqualTo(projectDir.resolve("build/distributions/my-project-shadow-1.0.0.tar")) } } } @@ -109,11 +197,13 @@ class ShadowPropertiesTest { val compileOnly = configurations.named(COMPILE_ONLY_CONFIGURATION_NAME).get() val gradleApi = dependencies.gradleApi() assertThat(api.dependencies).containsNone(gradleApi) - assertThat(compileOnly.dependencies).contains(gradleApi) + assertThat(compileOnly.dependencies).containsOnly(gradleApi) } private companion object { - const val PROJECT_NAME = "my-shadow" + const val PROJECT_NAME = "my-project" const val VERSION = "1.0.0" + + val Task.dependsOnTaskNames: List get() = dependsOn.filterIsInstance().map(Named::getName) } } From 036feecb6428eaf3326de6dcf6cfc141fb422657 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 21 Sep 2025 15:50:07 +0800 Subject: [PATCH 780/941] Support relocating Kotlin module files (#1539) Co-authored-by: phinner <62483793+phinner@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- build.gradle.kts | 1 + docs/changes/README.md | 3 + gradle/libs.versions.toml | 1 + .../gradle/plugins/shadow/RelocationTest.kt | 86 ++++++++++++++++++ .../gradle/plugins/shadow/util/JarBuilder.kt | 8 +- .../META-INF/kotlin-stdlib.kotlin_module | Bin 0 -> 7998 bytes .../plugins/shadow/tasks/ShadowCopyAction.kt | 52 +++++++++++ 7 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 src/functionalTest/resources/META-INF/kotlin-stdlib.kotlin_module diff --git a/build.gradle.kts b/build.gradle.kts index 78f9cc0a2..2516d86fc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -116,6 +116,7 @@ dependencies { implementation(libs.asm) implementation(libs.jdependency) implementation(libs.jdom2) + implementation(libs.kotlin.metadata) implementation(libs.plexus.utils) implementation(libs.plexus.xml) diff --git a/docs/changes/README.md b/docs/changes/README.md index a3dff6dd0..d8412e4ab 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -18,6 +18,9 @@ - Honor `executableDir` and `applicationName` in `application` extension. ([#1740](https://github.com/GradleUp/shadow/pull/1738)) This is useful when you want to customize the output directory of the start scripts and the application distribution. - Provide more task accessors in `ShadowApplicationPlugin.Companion`. ([#1771](https://github.com/GradleUp/shadow/pull/1771)) +- Support relocating Kotlin module files. ([#1539](https://github.com/GradleUp/shadow/pull/1539)) + The current implementation relocates all properties in `KotlinModuleMetadata` but `KmModule.optionalAnnotationClasses` + due to very limited usage of it. See more discussion [here](https://github.com/GradleUp/shadow/pull/1539#discussion_r2344237151). ### Changed diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ba69d8ea4..afd1fdd76 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,6 +13,7 @@ asm = "org.ow2.asm:asm-commons:9.8" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.13" jdom2 = "org.jdom:jdom2:2.0.6.1" +kotlin-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version.ref = "kotlin" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index e2e89cd3b..668fccd2e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -3,19 +3,26 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertFailure import assertk.assertThat import assertk.assertions.contains +import assertk.assertions.isEmpty import assertk.assertions.isEqualTo import assertk.assertions.isInstanceOf import assertk.assertions.isNotEmpty +import assertk.assertions.isNotEqualTo import assertk.fail import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowCopyAction.Companion.CONSTANT_TIME_FOR_ZIP_ENTRIES import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly import com.github.jengelman.gradle.plugins.shadow.testkit.getBytes +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsStream import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.runProcess import java.net.URLClassLoader import kotlin.io.path.appendText +import kotlin.io.path.readBytes import kotlin.io.path.writeText +import kotlin.metadata.jvm.KotlinModuleMetadata +import kotlin.metadata.jvm.UnstableMetadataApi import kotlin.time.Duration.Companion.seconds import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest @@ -593,6 +600,85 @@ class RelocationTest : BasePluginTest() { assertThat(relocatedBytes).isEqualTo(originalBytes) } + @Issue( + "https://github.com/GradleUp/shadow/issues/843", + ) + @Test + @OptIn(UnstableMetadataApi::class) + fun relocateKotlinModuleFiles() { + val originalModuleFilePath = "META-INF/kotlin-stdlib.kotlin_module" + val stdlibJar = buildJar("stdlib.jar") { + insert(originalModuleFilePath, requireResourceAsPath(originalModuleFilePath).readBytes()) + } + projectScript.appendText( + """ + dependencies { + ${implementationFiles(stdlibJar)} + } + $shadowJarTask { + relocate('kotlin', 'my.kotlin') + } + """.trimIndent(), + ) + + run(shadowJarPath) + + val relocatedModuleFilePath = "META-INF/kotlin-stdlib.shadow.kotlin_module" + assertThat(outputShadowedJar).useAll { + containsOnly( + relocatedModuleFilePath, + *manifestEntries, + ) + } + + val originalModule = KotlinModuleMetadata.read(requireResourceAsStream(originalModuleFilePath).readBytes()) + val relocatedModule = outputShadowedJar.use { + KotlinModuleMetadata.read(it.getBytes(relocatedModuleFilePath)) + } + + assertThat(relocatedModule.version.toString()).isEqualTo("2.2.0") + assertThat(originalModule.version.toString()).isEqualTo("2.2.0") + + // No implementation for writing the optionalAnnotationClasses property yet. + // https://github.com/JetBrains/kotlin/blob/81502985ae0a2f5b21e121ffc180c3f4dd467e17/libraries/kotlinx-metadata/jvm/src/kotlin/metadata/jvm/KotlinModuleMetadata.kt#L71 + assertThat(relocatedModule.kmModule.optionalAnnotationClasses).isEmpty() + + val originalPkgParts = originalModule.kmModule.packageParts.entries + val relocatedPkgParts = relocatedModule.kmModule.packageParts.entries + // They are not empty and different. + assertThat(originalPkgParts).isNotEqualTo(relocatedPkgParts) + assertThat(originalPkgParts.size).isEqualTo(relocatedPkgParts.size) + + relocatedPkgParts.forEachIndexed { index, (relocatedPkg, relocatedParts) -> + val (originalPkg, originalParts) = originalPkgParts.elementAt(index) + assertThat(relocatedPkg).isNotEqualTo(originalPkg) + assertThat(relocatedPkg).isEqualTo(originalPkg.replace("kotlin", "my.kotlin")) + + if (originalParts.fileFacades.isEmpty()) { + assertThat(relocatedParts.fileFacades).isEmpty() + } else { + assertThat(relocatedParts.fileFacades).isNotEmpty() + assertThat(relocatedParts.fileFacades).isNotEqualTo(originalParts.fileFacades) + assertThat(relocatedParts.fileFacades).isEqualTo( + originalParts.fileFacades.map { it.replace("kotlin/", "my/kotlin/") }, + ) + } + + if (originalParts.multiFileClassParts.isEmpty()) { + assertThat(relocatedParts.multiFileClassParts).isEmpty() + } else { + assertThat(relocatedParts.multiFileClassParts).isNotEmpty() + assertThat(relocatedParts.multiFileClassParts).isNotEqualTo(originalParts.multiFileClassParts) + assertThat(relocatedParts.multiFileClassParts).isEqualTo( + originalParts.multiFileClassParts.entries.associateTo(mutableMapOf()) { (name, facade) -> + name.replace("kotlin/", "my/kotlin/") to + facade.replace("kotlin/", "my/kotlin/") + }, + ) + } + } + } + private fun writeClassWithStringRef() { writeClass { """ diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt index 6da7edaf8..3fc1eb220 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt @@ -8,11 +8,13 @@ import kotlin.io.path.outputStream class JarBuilder( private val outputPath: Path, ) { - private val contents = mutableMapOf() + private val contents = mutableMapOf() private val entries = mutableSetOf() private val jos = JarOutputStream(outputPath.outputStream()) - fun insert(entry: String, content: String): JarBuilder = apply { + fun insert(entry: String, content: String): JarBuilder = insert(entry, content.toByteArray()) + + fun insert(entry: String, content: ByteArray): JarBuilder = apply { contents[entry] = content } @@ -25,7 +27,7 @@ class JarBuilder( } if (entries.add(entry)) { jos.putNextEntry(JarEntry(entry)) - content.byteInputStream().use { it.copyTo(jos) } + content.inputStream().use { it.copyTo(jos) } } } } diff --git a/src/functionalTest/resources/META-INF/kotlin-stdlib.kotlin_module b/src/functionalTest/resources/META-INF/kotlin-stdlib.kotlin_module new file mode 100644 index 0000000000000000000000000000000000000000..aa2c0b61c78402e80bfcd2d02e2a3ba31239c80b GIT binary patch literal 7998 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 literal 0 HcmV?d00001 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 5587f1df3..b62a7fac4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -4,12 +4,17 @@ import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper import com.github.jengelman.gradle.plugins.shadow.internal.cast import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator +import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import com.github.jengelman.gradle.plugins.shadow.relocation.relocatePath import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext import java.io.File import java.util.GregorianCalendar import java.util.zip.ZipException +import kotlin.metadata.jvm.KmModule +import kotlin.metadata.jvm.KmPackageParts +import kotlin.metadata.jvm.KotlinModuleMetadata +import kotlin.metadata.jvm.UnstableMetadataApi import org.apache.tools.zip.UnixStat import org.apache.tools.zip.Zip64RequiredException import org.apache.tools.zip.ZipEntry @@ -166,6 +171,13 @@ public open class ShadowCopyAction( fileDetails.remapClass() } } + path.endsWith(".kotlin_module") -> { + if (relocators.isEmpty()) { + fileDetails.writeToZip(path) + } else { + fileDetails.remapKotlinModule() + } + } else -> { val relocated = relocators.relocatePath(path) if (transform(fileDetails, relocated)) return @@ -230,6 +242,46 @@ public open class ShadowCopyAction( } } + /** + * Applies remapping to the given kotlin module with the specified relocation path. + * The remapped module is then written to the zip file. + */ + @OptIn(UnstableMetadataApi::class) + private fun FileCopyDetails.remapKotlinModule() = file.readBytes().let { bytes -> + val kmMetadata = KotlinModuleMetadata.read(bytes) + val newKmModule = KmModule().apply { + // We don't need to relocate the nested properties in `optionalAnnotationClasses`, there is a very special use case for Kotlin Multiplatform. + optionalAnnotationClasses += kmMetadata.kmModule.optionalAnnotationClasses + packageParts += kmMetadata.kmModule.packageParts.map { (pkg, parts) -> + val relocatedPkg = relocators.relocateClass(pkg) + val relocatedParts = KmPackageParts( + parts.fileFacades.mapTo(mutableListOf()) { relocators.relocatePath(it) }, + parts.multiFileClassParts.entries.associateTo(mutableMapOf()) { (name, facade) -> + relocators.relocatePath(name) to relocators.relocatePath(facade) + }, + ) + relocatedPkg to relocatedParts + } + } + val newKmMetadata = KotlinModuleMetadata(newKmModule, kmMetadata.version) + + val newBytes = newKmMetadata.write() + val relocatedPath = relocators.relocatePath(path) + val entryName = when { + relocatedPath != path -> relocatedPath + // Nothing changed, so keep the original path. + newBytes.contentEquals(bytes) -> path + // Content changed but path didn't, so rename to avoid name clash. The filename does not matter to the compiler. + else -> path.replace(".kotlin_module", ".shadow.kotlin_module") + } + val entry = zipEntry(entryName, preserveFileTimestamps, lastModified) { + unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() + } + zipOutStr.putNextEntry(entry) + zipOutStr.write(newBytes) + zipOutStr.closeEntry() + } + private fun transform(fileDetails: FileCopyDetails, path: String): Boolean { val transformer = transformers.find { it.canTransformResource(fileDetails) } ?: return false fileDetails.file.inputStream().use { inputStream -> From df9f164ec355a83ef1fb7c003aca1f5a2b17c9de Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 21 Sep 2025 16:36:18 +0800 Subject: [PATCH 781/941] Ignore local.properties --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 38247f4a6..604e8c503 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,9 @@ .kotlin build +### Android Studio ### +local.properties + ### IntelliJ IDEA ### .idea *.iws From 42ac08ad27ab7e4f2d73a81c2f8f9bdaffbe325f Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 21 Sep 2025 20:28:39 +0800 Subject: [PATCH 782/941] Remove outdated TODO for Gradle issue 22779 --- .github/workflows/release.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7e4ef9dd4..f8dedc831 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,6 @@ jobs: - uses: gradle/actions/setup-gradle@v4 with: cache-read-only: true - # TODO: https://github.com/gradle/gradle/issues/22779 - run: ./gradlew publishToMavenCentral publishPlugins env: GRADLE_PUBLISH_KEY: ${{ secrets.GRADLE_KEY }} From fbd4aacfb83f3b56b3292045d10a40ba410a275b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 22 Sep 2025 13:39:09 +0800 Subject: [PATCH 783/941] Delete .github/ISSUE_TEMPLATE.md --- .github/ISSUE_TEMPLATE.md | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 07e0295fa..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,13 +0,0 @@ -Please check the [User Guide](https://gradleup.com/shadow) before submitting "how do I do 'x'?" questions! - -### Shadow Version - -### Gradle Version - -### Expected Behavior - -### Actual Behavior - -### Gradle Build Script(s) - -### Content of Shadow JAR (`jar tf ` - post link to GIST if too long) From 98262fe0710ad1c7ad797e1574373e5976a3d423 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 19:13:32 +0800 Subject: [PATCH 784/941] Update dependency org.apache.logging.log4j:log4j-core to v2.25.2 (#1775) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index afd1fdd76..8edab3198 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ pluginPublish = "2.0.0" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.20.0" -apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.1" +apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.2" apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.11" asm = "org.ow2.asm:asm-commons:9.8" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. From 35c5fdbeab3592b254ea13d1357c32ed75a83ea0 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 23 Sep 2025 11:08:22 +0800 Subject: [PATCH 785/941] Revert "Create copilot-instructions.md with project details (#1761)" This reverts commit 1b6d701d452f8747f3f451bc8d2143459bd08789. --- .github/copilot-instructions.md | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md deleted file mode 100644 index 81be83977..000000000 --- a/.github/copilot-instructions.md +++ /dev/null @@ -1,25 +0,0 @@ -# Project Overview - -This project is a Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. - -## Folder Structure - -- `/src`: Contains the source code for the main logic and tests. -- `/api`: Contains the API dumps by the Kotlin compiler, which always reflect the public APIs in `/src/main/kotlin`. -- `/docs`: Contains documentation for the project, including API specifications and user guides. They will be generated by MkDocs to serve websites. - -## Libraries and Frameworks - -- Spotless for code style check. -- Dokka for Kdoc and API HTML generation. -- MkDocs for markdown render. -- ASM for modifying bytecode in `RelocatorRemapper`. -- The main logic in `ShadowJar` extends Gradle's `Jar`, which is also a subclass of `Copy` task. - -## Coding Standards - -- Apply indent sizes of 2 for all code. - -## Document guidelines - -- Check and refine the newly added documents and comment wording. From 2c0e8e6fe91fd5cef95a983f7fe37adf9bba2b73 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 23 Sep 2025 11:19:54 +0800 Subject: [PATCH 786/941] Document embedding non-JAR dependencies (#1777) * Rename `Embedding Jar Files Inside Your Shadow Jar` * See also for `Adding Extra Files` * Document `Embedding Non-JAR Dependencies Into Your Shadowed Jar` * Test `addDependenciesViaCustomConfigurationWithoutUnzipping` * Wording --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/configuration/README.md | 1 + docs/configuration/dependencies/README.md | 74 ++++++++++++++++++- .../gradle/plugins/shadow/JavaPluginsTest.kt | 26 +++++++ 3 files changed, 100 insertions(+), 1 deletion(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index 91ceaed8d..e81d1a2a6 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -210,6 +210,7 @@ method can be used to add extra files. } ``` +See also [Embedding Local Jar Files Into Your Shadowed Jar](dependencies/README.md#embedding-local-jar-files-into-your-shadowed-jar). [Jar.from]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action) diff --git a/docs/configuration/dependencies/README.md b/docs/configuration/dependencies/README.md index c807de2df..8ba2b6322 100644 --- a/docs/configuration/dependencies/README.md +++ b/docs/configuration/dependencies/README.md @@ -31,7 +31,7 @@ This means any dependency declared in the `runtimeOnly` configuration would be * > not have the intended effect, as `configurations.compile` will try to delegate to the > [`configurations`][ShadowJar.configurations] property of the [`ShadowJar`][ShadowJar] task instead of the `project` -## Embedding Jar Files Inside Your Shadow Jar +## Embedding Local Jar Files into Your Shadowed JAR The [`ShadowJar`][ShadowJar] task is a subclass of the [`Jar`][Jar] task, which means that the [`Jar.from`][Jar.from] method can be used to add extra files. @@ -94,6 +94,78 @@ Someone may need the unzipped `bar.jar` to be bundled, try out [`zipTree`][Proje See also [Adding Extra Files](../README.md#adding-extra-files) +## Embedding Non-JAR Dependencies into Your Shadowed JAR + +Dependencies added into `runtimeClasspath` configuration (`api`, `implementation`, `runtimeOnly`) will be unzipped and +merged into the shadowed JAR. Not all dependencies are JAR files, e.g. some of them are +[POM files](https://repo1.maven.org/maven2/org/graalvm/polyglot/js-community/24.2.2/), +[SO files](https://repo1.maven.org/maven2/io/github/ganadist/sqlite4java/libsqlite4java-osx-aarch64/1.0.392/), +and so on. If such dependencies are added into `runtimeClasspath`, you will encounter the following error when building +the shadowed JAR: + +``` +* What went wrong: +Execution failed for task ':shadowJar'. +> Cannot expand ZIP '/home/user/.gradle/caches/modules-2/files-2.1/some/of/non/jar/file.pom'. +... +Caused by: java.util.zip.ZipException: Archive is not a ZIP archive + at org.apache.commons.compress.archivers.zip.ZipFile.positionAtEndOfCentralDirectoryRecord(ZipFile.java:562) + at org.apache.commons.compress.archivers.zip.ZipFile.openZipChannel(ZipFile.java:504) + at org.apache.commons.compress.archivers.zip.ZipFile.access$000(ZipFile.java:88) + at org.apache.commons.compress.archivers.zip.ZipFile$Builder.get(ZipFile.java:159) + at org.gradle.api.internal.file.archive.ZipFileTree.lambda$visit$0(ZipFileTree.java:97) + ... 146 more +``` + +To embed such dependencies into your shadowed JAR, you can use the [`Jar.from`][Jar.from] method with a custom +configuration. + +=== "Kotlin" + + ```kotlin + val nonJar by configurations.creating + + // This is necessary to make the dependencies in `nonJar` available at compile time. + // If you don't need that, you can skip this step. + configurations.named("compileClasspath") { + extendsFrom(nonJar) + } + + dependencies { + nonJar("org.graalvm.js:js-community:24.2.2") + nonJar("io.github.ganadist.sqlite4java:libsqlite4java-osx-aarch64:1.0.392") + // If you add a real JAR file into the new `nonJar` configuration, it will be included as-is. Different from `implementation`. + nonJar(files("foo.jar")) + } + + tasks.shadowJar { + from(nonJar) + } + ``` + +=== "Groovy" + + ```groovy + def nonJar = configurations.create('nonJar') + + // This is necessary to make the dependencies in `nonJar` available at compile time. + // If you don't need that, you can skip this step. + configurations.compileClasspath { + extendsFrom nonJar + } + + dependencies { + add('nonJar', 'org.graalvm.js:js-community:24.2.2') + add('nonJar', 'io.github.ganadist.sqlite4java:libsqlite4java-osx-aarch64:1.0.392') + // If you add a real JAR file into the new `nonJar` configuration, it will be included as-is. Different from `implementation`. + add('nonJar', files('foo.jar')) + } + + tasks.shadowJar { + from(nonJar) + } + ``` + ## Filtering Dependencies Individual dependencies can be filtered from the final JAR by using the `dependencies` block of a diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index b4cbc63cf..dc73cbe51 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -788,6 +788,32 @@ class JavaPluginsTest : BasePluginTest() { } } + @Test + fun addDependenciesViaCustomConfigurationWithoutUnzipping() { + projectScript.appendText( + """ + def nonJar = configurations.create('nonJar') + dependencies { + add('nonJar', 'my:a:1.0') + add('nonJar', 'my:b:1.0') + } + $shadowJarTask { + from(nonJar) + } + """.trimIndent(), + ) + + run(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + containsOnly( + "a-1.0.jar", + "b-1.0.jar", + *manifestEntries, + ) + } + } + @Issue( "https://github.com/GradleUp/shadow/issues/520", ) From b6132335ff40fbdc222bd30f5fd161c677131043 Mon Sep 17 00:00:00 2001 From: Jonathing Date: Wed, 24 Sep 2025 02:54:43 -0400 Subject: [PATCH 787/941] Allow overriding BUNDLING_ATTRIBUTE in GMM (#1773) Co-authored-by: Zongle Wang Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 1 + docs/changes/README.md | 8 ++++++ docs/publishing/README.md | 22 +++++++++++++++ .../gradle/plugins/shadow/PublishingTest.kt | 27 +++++++++++++++++++ .../gradle/plugins/shadow/ShadowBasePlugin.kt | 2 ++ .../gradle/plugins/shadow/ShadowExtension.kt | 10 +++++++ .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 5 +++- .../plugins/shadow/ShadowPropertiesTest.kt | 2 ++ 8 files changed, 76 insertions(+), 1 deletion(-) diff --git a/api/shadow.api b/api/shadow.api index f48941658..5e2efff0c 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -43,6 +43,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$C public abstract interface class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { public abstract fun getAddShadowVariantIntoJavaComponent ()Lorg/gradle/api/provider/Property; public abstract fun getAddTargetJvmVersionAttribute ()Lorg/gradle/api/provider/Property; + public abstract fun getBundlingAttribute ()Lorg/gradle/api/provider/Property; } public abstract class com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin : org/gradle/api/Plugin { diff --git a/docs/changes/README.md b/docs/changes/README.md index d8412e4ab..616b359bf 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -21,6 +21,14 @@ - Support relocating Kotlin module files. ([#1539](https://github.com/GradleUp/shadow/pull/1539)) The current implementation relocates all properties in `KotlinModuleMetadata` but `KmModule.optionalAnnotationClasses` due to very limited usage of it. See more discussion [here](https://github.com/GradleUp/shadow/pull/1539#discussion_r2344237151). +- Allow overriding `BUNDLING_ATTRIBUTE` in GMM. ([#1773](https://github.com/GradleUp/shadow/pull/1773)) + The `org.gradle.dependency.bundling` in shadowed JAR's Gradle Module Metadata is set to `shadowed` by default. + You can override it for now by: + ```kotlin + shadow { + bundlingAttribute = Bundling.EMBEDDED + } + ``` ### Changed diff --git a/docs/publishing/README.md b/docs/publishing/README.md index a2be4715c..bec717df9 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -127,6 +127,28 @@ you can disable this by setting the `addTargetJvmVersionAttribute` property in t } ``` +The BUNDLING attribute (`org.gradle.dependency.bundling`) of the shadowed variant is set to `shadowed` by default, +it is useful for consumers to distinguish between normal and shadowed dependencies. You can override this attribute by +setting the `bundlingAttribute` property in the `shadow` extension: + +=== "Kotlin" + + ```kotlin + shadow { + // Per description of the attribute, you should set it to either `Bundling.SHADOWED` or `Bundling.EMBEDDED`. + bundlingAttribute = Bundling.EMBEDDED + } + ``` + +=== "Groovy" + + ```groovy + shadow { + // Per description of the attribute, you should set it to either `Bundling.SHADOWED` or `Bundling.EMBEDDED`. + bundlingAttribute = Bundling.EMBEDDED + } + ``` + ## Shadow Configuration and Publishing The Shadow plugin provides a custom configuration (`configurations.shadow`) to specify diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 58f3eb93c..c7c31371d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -191,6 +191,33 @@ class PublishingTest : BasePluginTest() { ) } + @Test + fun overrideBundlingAttrInGradleMetadata() { + projectScript.appendText( + publishConfiguration( + projectBlock = """ + shadow { + bundlingAttribute = Bundling.EMBEDDED + } + """.trimIndent(), + shadowBlock = """ + archiveClassifier = '' + archiveBaseName = 'maven-all' + """.trimIndent(), + ), + ) + + publish() + + assertShadowVariantCommon( + gmm = gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")), + variantAttrs = commonVariantAttrs + arrayOf( + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EMBEDDED, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + ), + ) + } + @Test fun publishShadowJarInsteadOfJar() { projectScript.appendText( diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 43c1efddd..4ed334530 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -8,6 +8,7 @@ import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.attributes.Bundling import org.gradle.api.component.SoftwareComponentContainer import org.gradle.api.distribution.DistributionContainer import org.gradle.api.plugins.ExtensionContainer @@ -22,6 +23,7 @@ public abstract class ShadowBasePlugin : Plugin { with(extensions.create(EXTENSION_NAME, ShadowExtension::class.java)) { addShadowVariantIntoJavaComponent.convention(true) addTargetJvmVersionAttribute.convention(true) + bundlingAttribute.convention(Bundling.SHADOWED) } @Suppress("EagerGradleConfiguration") // this should be created eagerly. configurations.create(CONFIGURATION_NAME) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index 74ef80658..b74387e8a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow +import org.gradle.api.attributes.Bundling import org.gradle.api.attributes.java.TargetJvmVersion import org.gradle.api.provider.Property @@ -19,4 +20,13 @@ public interface ShadowExtension { * Defaults to `true`. */ public val addTargetJvmVersionAttribute: Property + + /** + * The [Bundling] attribute to use for the Gradle Module Metadata. + * + * Per description of the attribute, you should set it to either [Bundling.SHADOWED] or [Bundling.EMBEDDED]. + * + * Defaults to [Bundling.SHADOWED]. + */ + public val bundlingAttribute: Property } 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 1f30c01b3..6863c6353 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 @@ -61,7 +61,10 @@ public abstract class ShadowJavaPlugin @Inject constructor( LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(LibraryElements::class.java, LibraryElements.JAR), ) - attrs.attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling::class.java, Bundling.SHADOWED)) + attrs.attributeProvider( + Bundling.BUNDLING_ATTRIBUTE, + shadow.bundlingAttribute.map { attr -> objects.named(Bundling::class.java, attr) }, + ) } it.outgoing.artifact(tasks.shadowJar) } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index b407fc810..bda146dc6 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -25,6 +25,7 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.shad import org.gradle.api.Named import org.gradle.api.Project import org.gradle.api.Task +import org.gradle.api.attributes.Bundling import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.plugins.ApplicationPlugin import org.gradle.api.plugins.JavaPlugin @@ -59,6 +60,7 @@ class ShadowPropertiesTest { with(extensions.getByType(ShadowExtension::class.java)) { assertThat(addShadowVariantIntoJavaComponent.get()).isTrue() assertThat(addTargetJvmVersionAttribute.get()).isTrue() + assertThat(bundlingAttribute.get()).isEqualTo(Bundling.SHADOWED) } } From 637dd00a48ee277a785a72f0f4af2244b0cfd3f7 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 24 Sep 2025 14:56:02 +0800 Subject: [PATCH 788/941] Prepare version 9.2.0 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index d8412e4ab..5cb3718b5 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.1.0...HEAD) - 2025-xx-xx +## [9.2.0](https://github.com/GradleUp/shadow/compare/9.2.0) - 2025-09-24 ### Added diff --git a/gradle.properties b/gradle.properties index 3b6a6ff52..7c938e80d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.1.1-SNAPSHOT +VERSION_NAME=9.2.0 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 698ff03689526ba51bbdecdc67e0df1f66e5ab7f Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 24 Sep 2025 14:56:52 +0800 Subject: [PATCH 789/941] Prepare next development version (cherry picked from commit 87ff5edda0126fd04935ec53b0edfec730edf560) --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 4d819b5be..b8433545e 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.2.0...HEAD) - 2025-xx-xx + + ## [9.2.0](https://github.com/GradleUp/shadow/compare/9.2.0) - 2025-09-24 ### Added diff --git a/gradle.properties b/gradle.properties index 7c938e80d..c1ff2fb50 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.2.0 +VERSION_NAME=9.2.1-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 75e4e0fb205da2b0c4d2574a89f3bd779b654da4 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 24 Sep 2025 15:02:11 +0800 Subject: [PATCH 790/941] Prepare version 9.2.1 --- docs/changes/README.md | 4 ++-- gradle.properties | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index b8433545e..84c8197f8 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,10 +1,10 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.2.0...HEAD) - 2025-xx-xx +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.2.1...HEAD) - 2025-xx-xx -## [9.2.0](https://github.com/GradleUp/shadow/compare/9.2.0) - 2025-09-24 +## [9.2.1](https://github.com/GradleUp/shadow/compare/9.2.0) - 2025-09-24 ### Added diff --git a/gradle.properties b/gradle.properties index c1ff2fb50..3bb21466a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.2.1-SNAPSHOT +VERSION_NAME=9.2.1 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 8f3a5e725f3befaec26ac821a22a93852b672675 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 24 Sep 2025 14:56:52 +0800 Subject: [PATCH 791/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3bb21466a..71c1c6cfb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.2.1 +VERSION_NAME=9.2.2-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 3474bea07dd46bcea31d55e1a9e81a29315734de Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 25 Sep 2025 08:45:59 +0800 Subject: [PATCH 792/941] Update plugin spotless to v8 (#1778) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8edab3198..809a9d5da 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,4 +39,4 @@ android-lint = "com.android.lint:8.13.0" jetbrains-dokka = "org.jetbrains.dokka:2.1.0-Beta" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } -spotless = "com.diffplug.spotless:7.2.1" +spotless = "com.diffplug.spotless:8.0.0" From bd7f137e0123c093302befa1ff204e64c76ede45 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 25 Sep 2025 14:50:19 +0800 Subject: [PATCH 793/941] Inline suppressions for InternalGradleApiUsage (#1780) * Suppress `InternalGradleApiUsage` for `DefaultInheritManifest` * Suppress `InternalGradleApiUsage` for `ShadowCopyAction` --- gradle/lint-baseline.xml | 143 ------------------ .../shadow/internal/DefaultInheritManifest.kt | 2 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 2 + .../gradle/plugins/shadow/tasks/ShadowJar.kt | 4 +- 4 files changed, 5 insertions(+), 146 deletions(-) diff --git a/gradle/lint-baseline.xml b/gradle/lint-baseline.xml index 398b73d9c..19930a6df 100644 --- a/gradle/lint-baseline.xml +++ b/gradle/lint-baseline.xml @@ -1,147 +1,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt index f807aa8a6..4ab6870fa 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt @@ -1,4 +1,4 @@ -@file:Suppress("DEPRECATION") +@file:Suppress("DEPRECATION", "InternalGradleApiUsage") // TODO: remove this in Shadow 10. package com.github.jengelman.gradle.plugins.shadow.internal diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index b62a7fac4..a5cdac30c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -1,3 +1,5 @@ +@file:Suppress("InternalGradleApiUsage") // We have to use internal Gradle APIs to implement a CopyAction. + package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.internal.RelocatorRemapper diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 904b039c4..c2e1cc506 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -41,7 +41,6 @@ import org.gradle.api.file.DuplicatesStrategy.FAIL import org.gradle.api.file.DuplicatesStrategy.INCLUDE import org.gradle.api.file.DuplicatesStrategy.INHERIT import org.gradle.api.file.DuplicatesStrategy.WARN -import org.gradle.api.internal.file.copy.CopyAction import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.CacheableTask @@ -387,7 +386,8 @@ public abstract class ShadowJar : Jar() { super.copy() } - override fun createCopyAction(): CopyAction { + @Suppress("InternalGradleApiUsage") // For creating ShadowCopyAction. + override fun createCopyAction(): org.gradle.api.internal.file.copy.CopyAction { val zosProvider = { destination: File -> try { val entryCompressionMethod = when (entryCompression) { From 21f26132bf75b83c390c6ed1c21b12b73ed620c5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 25 Sep 2025 23:23:59 +0800 Subject: [PATCH 794/941] Document more about deprecated inheritFrom (#1783) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 84c8197f8..272e4806e 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -40,7 +40,19 @@ and `META-INF/groovy` will be merged into `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`. - Move injecting `Class-Path` manifest attr logic from `doFirst` into `copy`. ([#1720](https://github.com/GradleUp/shadow/pull/1720)) - Move injecting `Main-Class` manifest attr logic from `doFirst` into `copy`. ([#1724](https://github.com/GradleUp/shadow/pull/1724)) -- Deprecate `InheritManifest`. ([#1722](https://github.com/GradleUp/shadow/pull/1722)) +- Deprecate `InheritManifest` and `inheritFrom`. ([#1722](https://github.com/GradleUp/shadow/pull/1722)) + ```kotlin + tasks.shadowJar { + // Before (deprecated): + manifest.inheritFrom(tasks.jar.get().manifest) + + // After (recommended): + manifest.from(tasks.jar.get().manifest) + + // Note: You don't need to inherit the manifest from `jar` task as it's done by default for the `shadowJar` task. + // But if you want to inherit the manifest for your custom `ShadowJar` task, you still need to do it explicitly. + } + ``` - Use default `JavaExec` error message when main class is not set. ([#1725](https://github.com/GradleUp/shadow/pull/1725)) - Update `RelocatorRemapper` class pattern to cover more Java method descriptors. ([#1731](https://github.com/GradleUp/shadow/pull/1731)) - Stop using start script templates bundled in Shadow. ([#1738](https://github.com/GradleUp/shadow/pull/1738)) From f36fadf19065bd4c4db235b5f5bad4a1b7ec4dca Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 26 Sep 2025 11:50:05 +0800 Subject: [PATCH 795/941] Tweak manifest attribute tests (#1786) * Replace `isGreaterThan` with `isEqualTo` * Replace `manifest.attribute` calls * Revert "Replace `manifest.attribute` calls" This reverts commit 727403d010603fb6e50695ef3391dca19d8afbf8. * Replace `manifest.from` calls * Unit test `inheritManifestAttrsFromJars` * Unit test `inheritManifestMainClassFromJar` * Simplify `DefaultInheritManifest` * Document more about `Configuring the JAR Manifest` * Update docs/configuration/README.md --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/configuration/README.md | 20 +++---- .../gradle/plugins/shadow/JavaPluginsTest.kt | 58 +------------------ .../shadow/internal/DefaultInheritManifest.kt | 20 +------ .../plugins/shadow/ShadowPropertiesTest.kt | 48 +++++++++++++++ 4 files changed, 60 insertions(+), 86 deletions(-) diff --git a/docs/configuration/README.md b/docs/configuration/README.md index e81d1a2a6..c0d205521 100644 --- a/docs/configuration/README.md +++ b/docs/configuration/README.md @@ -91,17 +91,15 @@ When deploying a shadowed JAR as an execution JAR, it is important to note that ## Configuring the JAR Manifest -Beyond the automatic configuration of the `Class-Path` entry, the [`ShadowJar`][ShadowJar] manifest is configured in a -number of ways. First, the manifest for the [`ShadowJar`][ShadowJar] task is configured to __inherit__ from the -manifest of the standard [`Jar`][Jar] task. This means that any configuration performed on the [`Jar`][Jar] task -will propagate to the [`ShadowJar`][ShadowJar] tasks. +The [`ShadowJar`][ShadowJar] manifest is configured in a number of ways. First, the manifest for the `shadowJar` task +is configured to __inherit__ from the manifest of the standard `jar` task. === "Kotlin" ```kotlin tasks.jar { manifest { - attributes["Class-Path"] = "/libs/foo.jar" + attributes["Main-Class"] = "my.Main" } } ``` @@ -111,19 +109,19 @@ will propagate to the [`ShadowJar`][ShadowJar] tasks. ```groovy tasks.named('jar', Jar) { manifest { - attributes 'Class-Path': '/libs/foo.jar' + attributes 'Main-Class': 'my.Main' } } ``` -Inspecting the `META-INF/MANIFEST.MF` entry in the JAR file will reveal the following attribute: +Inspecting the `META-INF/MANIFEST.MF` entry in the JAR files will reveal the following attribute: ```property -Class-Path: /libs/foo.jar +Main-Class: my.Main ``` -If it is desired to inherit a manifest from a JAR task other than the standard [`Jar`][Jar] task, the `from` -methods on the `shadowJar.manifest` object can be used to configure the upstream. +If it is desired to merge a manifest from another [`Jar`][Jar] task, the `manifest.from` methods can be used to +configure the upstream. === "Kotlin" @@ -149,7 +147,7 @@ methods on the `shadowJar.manifest` object can be used to configure the upstream } tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { - manifest.from(testJar.get().manifest) + manifest.from testJar.get().manifest } ``` diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index dc73cbe51..05e9eb31c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -6,7 +6,6 @@ import assertk.assertions.contains import assertk.assertions.containsMatch import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo -import assertk.assertions.isGreaterThan import assertk.assertions.isNotEmpty import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull @@ -512,7 +511,7 @@ class JavaPluginsTest : BasePluginTest() { assertThat(outputShadowedJar).useAll { transform { actual -> actual.entries().toList().map { it.name }.filter { it.endsWith(".class") } } .single().isEqualTo("my/plugin/MyPlugin.class") - transform { it.mainAttrSize }.isGreaterThan(0) + transform { it.mainAttrSize }.isEqualTo(1) // Doesn't contain Gradle classes. getMainAttr(classPathAttributeKey).isNull() @@ -687,61 +686,6 @@ class JavaPluginsTest : BasePluginTest() { ) } - @Test - fun inheritManifestAttrsFromJars() { - projectScript.appendText( - """ - $jarTask { - manifest { - attributes 'Foo-Attr': 'Foo-Value' - } - } - def testJar = tasks.register('testJar', Jar) { - manifest { - attributes 'Bar-Attr': 'Bar-Value' - } - } - $shadowJarTask { - manifest.from(testJar.get().manifest) - } - """.trimIndent(), - ) - - run(shadowJarPath) - - assertThat(outputShadowedJar).useAll { - transform { it.mainAttrSize }.isGreaterThan(2) - getMainAttr("Foo-Attr").isEqualTo("Foo-Value") - getMainAttr("Bar-Attr").isEqualTo("Bar-Value") - } - } - - @Test - fun inheritManifestMainClassFromJar() { - projectScript.appendText( - """ - $jarTask { - manifest { - attributes '$mainClassAttributeKey': 'my.Main' - } - } - $shadowJarTask { - mainClass = 'my.Main2' // This should not override the inherited one. - } - """.trimIndent(), - ) - - val result = run(shadowJarPath, infoArgument) - - assertThat(result.output).contains( - "Skipping adding $mainClassAttributeKey attribute to the manifest as it is already set.", - ) - assertThat(outputShadowedJar).useAll { - transform { it.mainAttrSize }.isGreaterThan(1) - getMainAttr("Main-Class").isEqualTo("my.Main") - } - } - @Test fun addExtraFilesViaFrom() { val mainClassEntry = writeClass() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt index 4ab6870fa..247cafbbf 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt @@ -10,7 +10,6 @@ import org.gradle.api.internal.project.DefaultProject import org.gradle.api.java.archives.Manifest import org.gradle.api.java.archives.ManifestMergeSpec import org.gradle.api.java.archives.internal.DefaultManifest -import org.gradle.api.java.archives.internal.DefaultManifestMergeSpec internal class DefaultInheritManifest( project: Project, @@ -21,28 +20,13 @@ internal class DefaultInheritManifest( private val internalManifest: Manifest = manifest ?: DefaultManifest(fileResolver), ) : InheritManifest, Manifest by internalManifest { - private val inheritMergeSpecs = mutableListOf() override fun inheritFrom( vararg inheritPaths: Any, action: Action, ) { - val mergeSpec = DefaultManifestMergeSpec() - mergeSpec.from(*inheritPaths) - inheritMergeSpecs.add(mergeSpec) - action.execute(mergeSpec) - } - - override fun getEffectiveManifest(): DefaultManifest { - var base = DefaultManifest(fileResolver) - inheritMergeSpecs.forEach { - base = it.merge(base, fileResolver) + inheritPaths.forEach { + from(it, action) } - base.from(internalManifest) - return base.effectiveManifest - } - - override fun writeTo(path: Any): Manifest = apply { - effectiveManifest.writeTo(path) } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index bda146dc6..aed55a4a9 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -18,6 +18,7 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.sha import com.github.jengelman.gradle.plugins.shadow.internal.applicationExtension import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension import com.github.jengelman.gradle.plugins.shadow.internal.javaToolchainService +import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.legacy.LegacyShadowPlugin import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME @@ -31,6 +32,8 @@ import org.gradle.api.plugins.ApplicationPlugin import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME +import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.AbstractArchiveTask import org.gradle.api.tasks.bundling.Jar import org.gradle.language.base.plugins.LifecycleBasePlugin @@ -64,6 +67,49 @@ class ShadowPropertiesTest { } } + @Test + fun inheritManifestAttrsFromJars() = with(project) { + plugins.apply(JavaPlugin::class.java) + tasks.jar.configure { + it.manifest.attributes["jar"] = "fromJar" + } + val jar1 = tasks.register("jar1", Jar::class.java) { + it.manifest.attributes["jar1"] = "fromJar1" + } + val jar2 = tasks.register("jar2", Jar::class.java) { + it.manifest.attributes["jar2"] = "fromJar2" + } + tasks.shadowJar.configure { + it.manifest.attributes["shadowJar"] = "fromShadowJar" + it.manifest.from(jar1.get().manifest) + @Suppress("DEPRECATION") // TODO: remove this once InheritManifest is removed. + it.manifest.inheritFrom(jar2.get().manifest) + } + // Call effectiveManifest as a way to force merging to happen like writing the jar would. + assertThat(tasks.shadowJar.get().manifest.effectiveManifest.attributes).containsOnly( + "Manifest-Version" to "1.0", + "jar" to "fromJar", + "jar1" to "fromJar1", + "jar2" to "fromJar2", + "shadowJar" to "fromShadowJar", + ) + } + + @Test + fun inheritManifestMainClassFromJar() = with(project) { + plugins.apply(JavaPlugin::class.java) + tasks.jar.configure { + it.manifest.attributes[mainClassAttributeKey] = "Main" + } + tasks.shadowJar.configure { + it.mainClass.set("Main2") // This should not override the inherited one from jar. + } + assertThat(tasks.shadowJar.get().manifest.attributes).containsOnly( + "Manifest-Version" to "1.0", + mainClassAttributeKey to "Main", + ) + } + @Test fun applyJavaPlugin() = with(project) { plugins.apply(JavaPlugin::class.java) @@ -207,5 +253,7 @@ class ShadowPropertiesTest { const val VERSION = "1.0.0" val Task.dependsOnTaskNames: List get() = dependsOn.filterIsInstance().map(Named::getName) + + val TaskContainer.jar: TaskProvider get() = named("jar", Jar::class.java) } } From 722b9b883acba387edc15b7195828e5aeb2ab4ea Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 26 Sep 2025 17:24:53 +0800 Subject: [PATCH 796/941] Fix the regression of registering ShadowJar tasks without ShadowPlugin applied (#1787) * Test `registerShadowJarTaskWithoutShadowPluginApplied` * Fix `shadowDependencies` * Update changelog * Cleanups --- docs/changes/README.md | 3 + .../gradle/plugins/shadow/BasePluginTest.kt | 3 +- .../gradle/plugins/shadow/JavaPluginsTest.kt | 56 +++++++++++++++++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 5 +- 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 272e4806e..04b149f67 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.2.1...HEAD) - 2025-xx-xx +## Fixed + +- Fix the regression of registering `ShadowJar` tasks without `ShadowPlugin` applied. ([#1787](https://github.com/GradleUp/shadow/pull/1787)) ## [9.2.1](https://github.com/GradleUp/shadow/compare/9.2.0) - 2025-09-24 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 ec6b2eeaa..d5d5917d5 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 @@ -144,13 +144,14 @@ abstract class BasePluginTest { plugin: String = "java", withGroup: Boolean = false, withVersion: Boolean = false, + applyShadowPlugin: Boolean = true, ): String { val groupInfo = if (withGroup) "group = 'my'" else "" val versionInfo = if (withVersion) "version = '1.0'" else "" return """ plugins { id '$plugin' - id '$shadowPluginId' + id '$shadowPluginId' apply $applyShadowPlugin } $groupInfo $versionInfo diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 05e9eb31c..1a4c34fc4 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -604,6 +604,62 @@ class JavaPluginsTest : BasePluginTest() { ) } + @Issue( + "https://github.com/GradleUp/shadow/issues/1784", + ) + @Test + fun registerShadowJarTaskWithoutShadowPluginApplied() { + val mainClassEntry = writeClass(sourceSet = "test", withImports = true) + val testShadowJarTask = "testShadowJar" + projectScript.writeText( + """ + ${getDefaultProjectBuildScript(withGroup = true, withVersion = true, applyShadowPlugin = false)} + dependencies { + testImplementation 'junit:junit:3.8.2' + } + def $testShadowJarTask = tasks.register('$testShadowJarTask', ${ShadowJar::class.java.name}) { + description = 'Create a combined JAR of project and test dependencies' + archiveClassifier = 'test' + from sourceSets.named('test').map { it.output } + configurations = project.configurations.named('testRuntimeClasspath').map { [it] } + manifest { + attributes '$mainClassAttributeKey': 'my.Main' + } + } + afterEvaluate { + def hasShadowPlugin = plugins.hasPlugin('${ShadowPlugin::class.qualifiedName}') + def hasShadowBasePlugin = plugins.hasPlugin('${ShadowBasePlugin::class.qualifiedName}') + logger.lifecycle("Has ShadowPlugin: " + hasShadowPlugin) + logger.lifecycle("Has ShadowBasePlugin: " + hasShadowBasePlugin) + } + """.trimIndent(), + ) + + val result = run(testShadowJarTask) + + assertThat(result.output).contains( + "Has ShadowPlugin: false", + "Has ShadowBasePlugin: false", + ) + + assertThat(jarPath("build/libs/my-1.0-test.jar")).useAll { + containsOnly( + "my/", + mainClassEntry, + *junitEntries, + *manifestEntries, + ) + getMainAttr(mainClassAttributeKey).isNotNull() + } + + val pathString = path("build/libs/my-1.0-test.jar").toString() + val runningOutput = runProcess("java", "-jar", pathString, "foo") + assertThat(runningOutput).contains( + "Hello, World! (foo) from Main", + "Refs: junit.framework.Test", + ) + } + @Issue( "https://github.com/GradleUp/shadow/issues/443", ) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index c2e1cc506..685df526c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -63,7 +63,10 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin @CacheableTask public abstract class ShadowJar : Jar() { private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) - private val shadowDependencies = project.provider { project.files(project.configurations.shadow) } + private val shadowDependencies = project.provider { + // Find shadow configuration here instead of get, as the ShadowJar tasks could be registered without Shadow plugin applied. + project.configurations.findByName(ShadowBasePlugin.CONFIGURATION_NAME) ?: project.files() + } init { group = LifecycleBasePlugin.BUILD_GROUP From a9bf7b5e4b8cbf5b87a4ff044904c60f11a3ffe4 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 26 Sep 2025 17:26:35 +0800 Subject: [PATCH 797/941] Fix the link for 9.2.1 --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 04b149f67..ed0454165 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,7 +7,7 @@ - Fix the regression of registering `ShadowJar` tasks without `ShadowPlugin` applied. ([#1787](https://github.com/GradleUp/shadow/pull/1787)) -## [9.2.1](https://github.com/GradleUp/shadow/compare/9.2.0) - 2025-09-24 +## [9.2.1](https://github.com/GradleUp/shadow/compare/9.2.1) - 2025-09-24 ### Added From 1245cda0c17f10c9da73abb5ede230d556df90bd Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 26 Sep 2025 17:27:16 +0800 Subject: [PATCH 798/941] Prepare version 9.2.2 --- docs/changes/README.md | 5 ++++- gradle.properties | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ed0454165..42b7ccfcc 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,10 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.2.1...HEAD) - 2025-xx-xx +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.2.2...HEAD) - 2025-xx-xx + + +## [9.2.2](https://github.com/GradleUp/shadow/compare/9.2.2) - 2025-09-26 ## Fixed diff --git a/gradle.properties b/gradle.properties index 71c1c6cfb..3ef7d627a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.2.2-SNAPSHOT +VERSION_NAME=9.2.2 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From a48c053b3c72b9d54cbb6d99d4eb18471a70b977 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 26 Sep 2025 17:27:56 +0800 Subject: [PATCH 799/941] Prepare next development version --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 3ef7d627a..82b7edf50 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.2.2 +VERSION_NAME=9.2.3-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From dba664f2708515b1d55e52fbe83e2924b75f3a94 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 26 Sep 2025 17:30:46 +0800 Subject: [PATCH 800/941] Fix the fixed section --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 42b7ccfcc..ab2a780d0 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -6,7 +6,7 @@ ## [9.2.2](https://github.com/GradleUp/shadow/compare/9.2.2) - 2025-09-26 -## Fixed +### Fixed - Fix the regression of registering `ShadowJar` tasks without `ShadowPlugin` applied. ([#1787](https://github.com/GradleUp/shadow/pull/1787)) From 717bf3983622e1c83562d5e1f85a18eb4c5a0ad9 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 27 Sep 2025 16:32:37 +0800 Subject: [PATCH 801/941] Simplify getDefaultProjectBuildScript (#1788) * Tweak default values for `getDefaultProjectBuildScript` * Update `MinimizeTest` * Tweak calls for `getDefaultProjectBuildScript` --- .../gradle/plugins/shadow/BasePluginTest.kt | 12 ++++++------ .../jengelman/gradle/plugins/shadow/CachingTest.kt | 13 ++++--------- .../gradle/plugins/shadow/GroovyPluginTest.kt | 7 +------ .../gradle/plugins/shadow/JavaPluginsTest.kt | 4 ++-- .../gradle/plugins/shadow/KotlinPluginsTest.kt | 9 ++------- .../jengelman/gradle/plugins/shadow/MinimizeTest.kt | 9 ++++++--- .../gradle/plugins/shadow/ScalaPluginTest.kt | 7 +------ 7 files changed, 22 insertions(+), 39 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 d5d5917d5..90ed09384 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 @@ -125,7 +125,7 @@ abstract class BasePluginTest { @BeforeEach open fun beforeEach() { - projectScript.writeText(getDefaultProjectBuildScript(withGroup = true, withVersion = true)) + projectScript.writeText(getDefaultProjectBuildScript()) settingsScript.writeText(getDefaultSettingsBuildScript()) } @@ -142,8 +142,8 @@ abstract class BasePluginTest { fun getDefaultProjectBuildScript( plugin: String = "java", - withGroup: Boolean = false, - withVersion: Boolean = false, + withGroup: Boolean = true, + withVersion: Boolean = true, applyShadowPlugin: Boolean = true, ): String { val groupInfo = if (withGroup) "group = 'my'" else "" @@ -303,7 +303,7 @@ abstract class BasePluginTest { ) path("client/build.gradle").writeText( """ - ${getDefaultProjectBuildScript("java", withVersion = true)} + ${getDefaultProjectBuildScript("java")} dependencies { implementation 'junit:junit:3.8.2' } @@ -319,7 +319,7 @@ abstract class BasePluginTest { ) path("server/build.gradle").writeText( """ - ${getDefaultProjectBuildScript("java", withVersion = true)} + ${getDefaultProjectBuildScript("java")} dependencies { implementation project(':client') } @@ -353,7 +353,7 @@ abstract class BasePluginTest { fun writeGradlePluginModule() { projectScript.writeText( """ - ${getDefaultProjectBuildScript("java-gradle-plugin", withGroup = true, withVersion = true)} + ${getDefaultProjectBuildScript("java-gradle-plugin")} gradlePlugin { plugins { create('myPlugin') { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt index f2accb91e..3f4843d6c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -130,12 +130,12 @@ class CachingTest : BasePluginTest() { DuplicatesStrategy.WARN, ).forEach { strategy -> projectScript.writeText( - getDefaultProjectBuildScript(withGroup = true, withVersion = true) + - """ + """ + ${getDefaultProjectBuildScript()} $shadowJarTask { duplicatesStrategy = DuplicatesStrategy.$strategy } - """.trimIndent(), + """.trimIndent(), ) assertCompositeExecutions() @@ -191,14 +191,9 @@ class CachingTest : BasePluginTest() { val mainClassName = "my.Main" val main2ClassName = "my.Main2" - val projectBuildScript = getDefaultProjectBuildScript( - plugin = "org.jetbrains.kotlin.multiplatform", - withGroup = true, - withVersion = true, - ) projectScript.writeText( """ - $projectBuildScript + ${getDefaultProjectBuildScript(plugin = "org.jetbrains.kotlin.multiplatform")} kotlin { jvm().mainRun { it.mainClass.set('$mainClassName') diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt index 9b406fefa..e5b1061fa 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt @@ -12,12 +12,7 @@ class GroovyPluginTest : BasePluginTest() { @BeforeEach override fun beforeEach() { super.beforeEach() - val projectBuildScript = getDefaultProjectBuildScript( - plugin = "groovy", - withGroup = true, - withVersion = true, - ) - projectScript.writeText(projectBuildScript) + projectScript.writeText(getDefaultProjectBuildScript(plugin = "groovy")) } @Test diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 1a4c34fc4..334fd14e7 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -374,7 +374,7 @@ class JavaPluginsTest : BasePluginTest() { projectScript.writeText( """ - ${getDefaultProjectBuildScript("java-library", withGroup = true, withVersion = true)} + ${getDefaultProjectBuildScript("java-library")} dependencies { api 'my:api:1.0' implementation 'my:implementation:1.0' @@ -613,7 +613,7 @@ class JavaPluginsTest : BasePluginTest() { val testShadowJarTask = "testShadowJar" projectScript.writeText( """ - ${getDefaultProjectBuildScript(withGroup = true, withVersion = true, applyShadowPlugin = false)} + ${getDefaultProjectBuildScript(applyShadowPlugin = false)} dependencies { testImplementation 'junit:junit:3.8.2' } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 1122e76de..3b3c7c1e8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -21,12 +21,7 @@ class KotlinPluginsTest : BasePluginTest() { @BeforeEach override fun beforeEach() { super.beforeEach() - val projectBuildScript = getDefaultProjectBuildScript( - plugin = "org.jetbrains.kotlin.multiplatform", - withGroup = true, - withVersion = true, - ) - projectScript.writeText(projectBuildScript) + projectScript.writeText(getDefaultProjectBuildScript(plugin = "org.jetbrains.kotlin.multiplatform")) } @ParameterizedTest @@ -36,7 +31,7 @@ class KotlinPluginsTest : BasePluginTest() { projectScript.writeText( """ - ${getDefaultProjectBuildScript(plugin = "org.jetbrains.kotlin.jvm", withGroup = true, withVersion = true)} + ${getDefaultProjectBuildScript(plugin = "org.jetbrains.kotlin.jvm")} dependencies { implementation 'junit:junit:3.8.2' $stdlib diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index c33269841..57834736e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.assertThat import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME +import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.testkit.containsNone import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly @@ -13,6 +14,8 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource class MinimizeTest : BasePluginTest() { + private val outputImplShadowedJar: JarPath get() = jarPath("impl/build/libs/impl-1.0-all.jar") + /** * 'api' used as api for 'impl', and depended on 'lib'. 'junit' is independent. * The minimize step shall remove 'junit', but not 'api'. @@ -24,7 +27,7 @@ class MinimizeTest : BasePluginTest() { run(":impl:$SHADOW_JAR_TASK_NAME") - assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { + assertThat(outputImplShadowedJar).useAll { containsAtLeast( "api/", "lib/", @@ -58,7 +61,7 @@ class MinimizeTest : BasePluginTest() { run(":impl:$SHADOW_JAR_TASK_NAME") - assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { + assertThat(outputImplShadowedJar).useAll { containsOnly( "api/", "impl/", @@ -296,7 +299,7 @@ class MinimizeTest : BasePluginTest() { run(":impl:$SHADOW_JAR_TASK_NAME") - assertThat(jarPath("impl/build/libs/impl-all.jar")).useAll { + assertThat(outputImplShadowedJar).useAll { containsAtLeast( "api/", "lib/", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt index 9be358b36..ed3286d84 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt @@ -12,12 +12,7 @@ class ScalaPluginTest : BasePluginTest() { @BeforeEach override fun beforeEach() { super.beforeEach() - val projectBuildScript = getDefaultProjectBuildScript( - plugin = "scala", - withGroup = true, - withVersion = true, - ) - projectScript.writeText(projectBuildScript) + projectScript.writeText(getDefaultProjectBuildScript(plugin = "scala")) } @Test From c0e25bc8a84200b7aa2877a776c0eb50de51592d Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 28 Sep 2025 12:24:01 +0800 Subject: [PATCH 802/941] Disable check-links job for forks --- .github/workflows/links.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index fd87e128b..db0b92a82 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -20,6 +20,7 @@ on: jobs: check-links: runs-on: ubuntu-24.04-arm + if: github.event.repository.fork == false steps: - uses: actions/checkout@v5 - uses: gradle/actions/setup-gradle@v4 From 8f94d8e46a24c3ee3c049a6d4af90b926a14aaf8 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 29 Sep 2025 10:34:16 +0800 Subject: [PATCH 803/941] Tidy up jvmArgs for functionalTest (#1789) --- build.gradle.kts | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 2516d86fc..e0b2df0c8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -157,14 +157,10 @@ testing.suites { // Required to test configuration cache in tests when using withDebug(). // See https://github.com/gradle/gradle/issues/22765#issuecomment-1339427241. jvmArgs( - "--add-opens", - "java.base/java.util=ALL-UNNAMED", - "--add-opens", - "java.base/java.util.concurrent.atomic=ALL-UNNAMED", - "--add-opens", - "java.base/java.lang.invoke=ALL-UNNAMED", - "--add-opens", - "java.base/java.net=ALL-UNNAMED", + "--add-opens=java.base/java.util=ALL-UNNAMED", + "--add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED", + "--add-opens=java.base/java.lang.invoke=ALL-UNNAMED", + "--add-opens=java.base/java.net=ALL-UNNAMED", ) } } From b0ab8de3fedd4d3d2badcf0f0de9f5d2df06114b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 29 Sep 2025 21:19:55 +0800 Subject: [PATCH 804/941] Compile only on kotlin-gradle-plugin (#1790) --- build.gradle.kts | 4 ++-- gradle/libs.versions.toml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index e0b2df0c8..e92851222 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -108,7 +108,7 @@ val testGradleVersion: String = providers.gradleProperty("testGradleVersion").or dependencies { compileOnly(libs.develocity) - compileOnly(libs.kotlin.kmp) + compileOnly(libs.kotlin.gradlePlugin) compileOnly(libs.kotlin.reflect) api(libs.apache.ant) // Types from Ant are exposed in the public API. implementation(libs.apache.commonsIo) @@ -124,7 +124,7 @@ dependencies { testPluginClasspath(libs.foojayResolver) testPluginClasspath(libs.develocity) - testPluginClasspath(libs.kotlin.kmp) + testPluginClasspath(libs.kotlin.gradlePlugin) testPluginClasspath(libs.pluginPublish) lintChecks(libs.androidx.gradlePluginLints) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 809a9d5da..0a4f0a3a3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -23,7 +23,7 @@ moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "mosh foojayResolver = "org.gradle.toolchains.foojay-resolver-convention:org.gradle.toolchains.foojay-resolver-convention.gradle.plugin:1.0.0" develocity = "com.gradle:develocity-gradle-plugin:4.2" -kotlin-kmp = { module = "org.jetbrains.kotlin.multiplatform:org.jetbrains.kotlin.multiplatform.gradle.plugin", version.ref = "kotlin" } +kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" From caede73d34f11fd057876d63f72da393ec6ba241 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Sep 2025 10:16:45 +0000 Subject: [PATCH 805/941] Update dependency org.junit:junit-bom to v6.0.0 (#1791) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a4f0a3a3..d790bbf2d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.7.1" -junit-bom = "org.junit:junit-bom:6.0.0-RC3" +junit-bom = "org.junit:junit-bom:6.0.0" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From 88d88d2efeabbbd4b3754749d6bda0c9b3778f59 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 09:42:47 +0800 Subject: [PATCH 806/941] Update gradle/actions action to v5 (#1792) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 6 +++--- .github/workflows/deploy.yml | 2 +- .github/workflows/links.yml | 2 +- .github/workflows/release.yml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index be1beee01..23231a572 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -36,7 +36,7 @@ jobs: with: distribution: 'zulu' java-version: ${{ matrix.java }} - - uses: gradle/actions/setup-gradle@v4 + - uses: gradle/actions/setup-gradle@v5 - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" --stacktrace -x lint # TODO: https://issuetracker.google.com/issues/438361087 @@ -48,7 +48,7 @@ jobs: with: distribution: 'zulu' java-version: 21 - - uses: gradle/actions/setup-gradle@v4 + - uses: gradle/actions/setup-gradle@v5 - run: ./gradlew lint # Status check that is required in branch protection rules. @@ -78,7 +78,7 @@ jobs: with: distribution: 'zulu' java-version: 21 - - uses: gradle/actions/setup-gradle@v4 + - uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true - run: ./gradlew publishToMavenCentral diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index d7bf98331..dad254fd6 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -15,7 +15,7 @@ jobs: pages: write steps: - uses: actions/checkout@v5 - - uses: gradle/actions/setup-gradle@v4 + - uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true - name: Prepare API documentation diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index db0b92a82..1e90d5bca 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -23,7 +23,7 @@ jobs: if: github.event.repository.fork == false steps: - uses: actions/checkout@v5 - - uses: gradle/actions/setup-gradle@v4 + - uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true # This is required for action-linkspector to work with API links. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f8dedc831..6e866e8f1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: with: distribution: 'zulu' java-version: 21 - - uses: gradle/actions/setup-gradle@v4 + - uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true - run: ./gradlew publishToMavenCentral publishPlugins From 4ff97b3e74a35d19f9e50ecb2088e2d1be8e20c0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 08:48:57 +0000 Subject: [PATCH 807/941] Update Develocity to v4.2.1 (#1796) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d790bbf2d..0bc5a35b4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ 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:1.0.0" -develocity = "com.gradle:develocity-gradle-plugin:4.2" +develocity = "com.gradle:develocity-gradle-plugin:4.2.1" kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 243f33a45..d6d9cb882 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "4.2" + id("com.gradle.develocity") version "4.2.1" } develocity { From 2d8e9f502875fb258812f89e81d67fc201ba08bd Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 3 Oct 2025 19:55:48 +0800 Subject: [PATCH 808/941] Document disabling jar task when publishing the shadowed jar (#1795) --- docs/publishing/README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/docs/publishing/README.md b/docs/publishing/README.md index bec717df9..4c1e27828 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -303,6 +303,26 @@ the `archiveClassifier` of the shadowed JAR like the following: } ``` +Because the default `archiveClassifier` of [`Jar`][Jar] is `""` (empty), setting the `archiveClassifier` of +[`ShadowJar`][ShadowJar] to `""` (empty) will make collisions between the outputs of these two tasks in some cases. +If you don't need the standard JAR, you can disable the `jar` task like: + +=== "Kotlin" + + ```kotlin + tasks.jar { + enabled = false + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('jar', Jar) { + enabled = false + } + ``` + ## Publishing the Shadowed Gradle Plugins The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. From cf3c59c007bc85b8391912df56522d876e23ab25 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 3 Oct 2025 20:42:21 +0800 Subject: [PATCH 809/941] Change the group of startShadowScripts from application to other (#1797) Aligns with https://github.com/gradle/gradle/blob/fdecc3c95828bb9a1c1bb6114483fe5b16f9159d/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java#L183 --- docs/changes/README.md | 3 +++ .../jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt | 1 - .../jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index ab2a780d0..5cd36e305 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.2.2...HEAD) - 2025-xx-xx +### Changed + +- Change the group of `startShadowScripts` from `application` to `other`. ([#1797](https://github.com/GradleUp/shadow/pull/1797)) ## [9.2.2](https://github.com/GradleUp/shadow/compare/9.2.2) - 2025-09-26 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index f68ecb879..0f1879582 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -59,7 +59,6 @@ public abstract class ShadowApplicationPlugin : Plugin { protected open fun Project.addCreateScriptsTask() { tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { task -> task.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" - task.group = ApplicationPlugin.APPLICATION_GROUP task.classpath = files(tasks.shadowJar) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index aed55a4a9..6a886394b 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -181,7 +181,6 @@ class ShadowPropertiesTest { with(startShadowScripts) { assertThat(description).isEqualTo("Creates OS specific scripts to run the project as a JVM application using the shadow jar") - assertThat(group).isEqualTo(ApplicationPlugin.APPLICATION_GROUP) assertThat(classpath?.files).isNotNull().containsOnly(shadowJarTask.archiveFile.get().asFile) assertThat(mainModule.orNull).isEqualTo(applicationExtension.mainModule.orNull) assertThat(mainClass.orNull).isEqualTo(applicationExtension.mainClass.orNull) From 0e1008ffc5d5d51474180176951c8350dfaa5dee Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 6 Oct 2025 10:44:18 +0800 Subject: [PATCH 810/941] Gradle 9.2.0 RC1 (#1798) * https://docs.gradle.org/9.2.0-rc-1/release-notes.html * Add `addVariantsFromConfigurationCompat` * Defer `shadowRuntimeElements` * Remove `DisabledOnOs` * Fix `publishShadowJarWithCorrectTargetJvm` https://docs.gradle.org/9.2.0-rc-1/userguide/toolchains.html#sub:download_repositories * Revert "Fix `publishShadowJarWithCorrectTargetJvm`" This reverts commit 18cc54fbd7c6090ebb63a97587c3037cb4053d76. * Revert "Remove `DisabledOnOs`" This reverts commit 35bc7721f8ce91f5305ae258e352b892f559c591. * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowJavaPlugin.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 16 ++++++++------ .../plugins/shadow/internal/GradleCompat.kt | 22 +++++++++++++++++++ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 2e1113280..901ca4ae0 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-rc-1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME 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 6863c6353..f58b81e39 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 @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.SHADOW import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow +import com.github.jengelman.gradle.plugins.shadow.internal.addVariantsFromConfigurationCompat import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension import com.github.jengelman.gradle.plugins.shadow.internal.runtimeConfiguration import com.github.jengelman.gradle.plugins.shadow.internal.sourceSets @@ -49,8 +50,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME) { compileClasspath -> compileClasspath.extendsFrom(shadowConfiguration) } - @Suppress("EagerGradleConfiguration") // this should be created eagerly. - val shadowRuntimeElements = configurations.create(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { + val shadowRuntimeElements = configurations.register(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { it.extendsFrom(shadowConfiguration) it.isCanBeConsumed = true it.isCanBeResolved = false @@ -86,18 +86,20 @@ public abstract class ShadowJavaPlugin @Inject constructor( logger.info("Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases.") } else { logger.info("Setting target JVM version to $targetJvmVersion for ${shadowRuntimeElements.name} configuration.") - shadowRuntimeElements.attributes { attrs -> - attrs.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) + shadowRuntimeElements.configure { + it.attributes { attrs -> + attrs.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) + } } } } } protected open fun Project.configureComponents() { - val shadowRuntimeElements = configurations.shadowRuntimeElements.get() + val shadowRuntimeElements = configurations.shadowRuntimeElements val shadowComponent = softwareComponentFactory.adhoc(COMPONENT_NAME) components.add(shadowComponent) - shadowComponent.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> + shadowComponent.addVariantsFromConfigurationCompat(shadowRuntimeElements) { variant -> variant.mapToMavenScope("runtime") } // Must use afterEvaluate here as we need to track the changes of addShadowVariantIntoJavaComponent. @@ -109,7 +111,7 @@ public abstract class ShadowJavaPlugin @Inject constructor( return@afterEvaluate } components.named("java", AdhocComponentWithVariants::class.java) { - it.addVariantsFromConfiguration(shadowRuntimeElements) { variant -> + it.addVariantsFromConfigurationCompat(shadowRuntimeElements) { variant -> variant.mapToOptional() } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index e60a0c681..e6a744693 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -2,8 +2,13 @@ package com.github.jengelman.gradle.plugins.shadow.internal import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.gradle.develocity.agent.gradle.DevelocityConfiguration +import org.gradle.api.Action +import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Project import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ConsumableConfiguration +import org.gradle.api.component.AdhocComponentWithVariants +import org.gradle.api.component.ConfigurationVariantDetails import org.gradle.api.distribution.DistributionContainer import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.model.ObjectFactory @@ -16,6 +21,7 @@ import org.gradle.api.provider.Provider import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.SourceSetContainer import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.util.GradleVersion /** * Return `runtimeClasspath` or `runtime` configuration. @@ -53,6 +59,22 @@ internal fun Project.addBuildScanCustomValues() { } } +/** + * TODO: this could be removed after bumping the min Gradle requirement to 9.2 or above. + */ +@Suppress("UnstableApiUsage") +internal fun AdhocComponentWithVariants.addVariantsFromConfigurationCompat( + outgoingConfiguration: NamedDomainObjectProvider, + action: Action, +) { + if (GradleVersion.current() >= GradleVersion.version("9.2")) { + @Suppress("UNCHECKED_CAST") + addVariantsFromConfiguration(outgoingConfiguration as Provider, action) + } else { + addVariantsFromConfiguration(outgoingConfiguration.get(), action) + } +} + internal inline fun ObjectFactory.property( defaultValue: Any? = null, ): Property = property(V::class.java).apply { From 1660eea181d5438b4396f91916e267e37176eca6 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 8 Oct 2025 00:39:11 +0800 Subject: [PATCH 811/941] TODO apiVersion --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index e92851222..a3611562c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,7 +37,7 @@ kotlin { compilerOptions { allWarningsAsErrors = true // https://docs.gradle.org/current/userguide/compatibility.html#kotlin - apiVersion = KotlinVersion.KOTLIN_2_0 + apiVersion = KotlinVersion.KOTLIN_2_0 // TODO: upgrade to 2.2 when the min Gradle requirement is 9.0+ languageVersion = apiVersion jvmTarget = JvmTarget.fromTarget(libs.versions.jdkRelease.get()) jvmDefault = JvmDefaultMode.NO_COMPATIBILITY From c8b3636f7906b1ea4b5f326d464f1accce400605 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 8 Oct 2025 01:06:20 +0800 Subject: [PATCH 812/941] Fix test util packages (#1802) --- .../gradle/plugins/shadow/transformers/BaseTransformerTest.kt | 4 ++-- .../transformers/Log4j2PluginsCacheFileTransformerTest.kt | 2 +- .../plugins/shadow/transformers/ServiceFileTransformerTest.kt | 2 +- .../com/github/jengelman/gradle/plugins/shadow/util/Utils.kt | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index e3232b680..89d4c2859 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -2,10 +2,10 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath import com.github.jengelman.gradle.plugins.shadow.testkit.getStream -import com.github.jengelman.gradle.plugins.shadow.testkit.noOpDelegate import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsStream -import com.github.jengelman.gradle.plugins.shadow.testkit.testObjectFactory import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer.Companion.create +import com.github.jengelman.gradle.plugins.shadow.util.noOpDelegate +import com.github.jengelman.gradle.plugins.shadow.util.testObjectFactory import java.lang.reflect.ParameterizedType import java.util.Locale import kotlin.io.path.createTempFile diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index 45ea7c5fb..60636f030 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -12,7 +12,7 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsStream -import com.github.jengelman.gradle.plugins.shadow.testkit.zipOutputStream +import com.github.jengelman.gradle.plugins.shadow.util.zipOutputStream import java.io.ByteArrayOutputStream import java.net.URI import java.net.URL diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 971f2afff..11b6755d2 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -8,7 +8,7 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.SimpleRelocator import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath import com.github.jengelman.gradle.plugins.shadow.testkit.getContent -import com.github.jengelman.gradle.plugins.shadow.testkit.zipOutputStream +import com.github.jengelman.gradle.plugins.shadow.util.zipOutputStream import java.nio.file.Path import kotlin.io.path.createTempFile import kotlin.io.path.deleteExisting diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt index 53e52179c..0f7168379 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Utils.kt @@ -1,4 +1,4 @@ -package com.github.jengelman.gradle.plugins.shadow.testkit +package com.github.jengelman.gradle.plugins.shadow.util import java.io.OutputStream import java.lang.reflect.InvocationHandler From b68b8ef49e9c4de4056ccb7b262e6442c3d237a2 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 8 Oct 2025 23:02:55 +0800 Subject: [PATCH 813/941] Remove extra nesting (#1803) --- build.gradle.kts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a3611562c..0c1f8eb93 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -76,18 +76,16 @@ configurations.configureEach { RUNTIME_ELEMENTS_CONFIGURATION_NAME, JAVADOC_ELEMENTS_CONFIGURATION_NAME, SOURCES_ELEMENTS_CONFIGURATION_NAME, - -> { - outgoing { - // Main/current capability. - capability("com.gradleup.shadow:shadow-gradle-plugin:$version") - - // Historical capabilities. - capability("io.github.goooler.shadow:shadow-gradle-plugin:$version") - capability("com.github.johnrengelman:shadow:$version") - capability("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:$version") - capability("gradle.plugin.com.github.johnrengelman:shadow:$version") - capability("com.github.jengelman.gradle.plugins:shadow:$version") - } + -> outgoing { + // Main/current capability. + capability("com.gradleup.shadow:shadow-gradle-plugin:$version") + + // Historical capabilities. + capability("io.github.goooler.shadow:shadow-gradle-plugin:$version") + capability("com.github.johnrengelman:shadow:$version") + capability("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:$version") + capability("gradle.plugin.com.github.johnrengelman:shadow:$version") + capability("com.github.jengelman.gradle.plugins:shadow:$version") } } } From fdd198862c9a5b5045bab8af0c9d826935a0b6ed Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 8 Oct 2025 23:25:42 +0800 Subject: [PATCH 814/941] Set min Gradle requirement via GradlePluginApiVersion attribute (#1804) ```diff 24a25 > "org.gradle.plugin.api-version": "8.11", 41,45c42,46 < "size": 217573, < "sha512": "f14b96cd6fb11af08ea5be550e6e70c43a13933e8d54805aff1703be4e7d9afb402028352b3ec0829ee711f360c1e52c9948a9e24230d14f733fab4885a90913", < "sha256": "681603e58c691e87522c20ec0a8bf4620123929de04b0873f4412d95696d9898", < "sha1": "7bdec4422bc1a7154dc298427c625aeda137ac09", < "md5": "e94b96defed218255158cbe8327f8872" --- > "size": 217348, > "sha512": "0aac1c2d823806cb5e3642befea61368cf23de94891b019d489141e5b894a4e50d7b6309bfde0efacd846b1a21d9a0e59ad755c92cdc17ba7b28cb4348751331", > "sha256": "f10e9084b6186cc09f45c04018f1f6666810f4a76a7c775ea5609c30c0d8b99f", > "sha1": "4322e07e60c617ba8022e3f637d5f487aafefd4f", > "md5": "231f12084f9b0ff4181a6e67ce03045e" 161,165c162,166 < "size": 217573, < "sha512": "f14b96cd6fb11af08ea5be550e6e70c43a13933e8d54805aff1703be4e7d9afb402028352b3ec0829ee711f360c1e52c9948a9e24230d14f733fab4885a90913", < "sha256": "681603e58c691e87522c20ec0a8bf4620123929de04b0873f4412d95696d9898", < "sha1": "7bdec4422bc1a7154dc298427c625aeda137ac09", < "md5": "e94b96defed218255158cbe8327f8872" --- > "size": 217348, > "sha512": "0aac1c2d823806cb5e3642befea61368cf23de94891b019d489141e5b894a4e50d7b6309bfde0efacd846b1a21d9a0e59ad755c92cdc17ba7b28cb4348751331", > "sha256": "f10e9084b6186cc09f45c04018f1f6666810f4a76a7c775ea5609c30c0d8b99f", > "sha1": "4322e07e60c617ba8022e3f637d5f487aafefd4f", > "md5": "231f12084f9b0ff4181a6e67ce03045e" 265,269c266,270 < "size": 58425, < "sha512": "423dc273943386a394c8d837731e48c79a3cf85d389106d58e1f93b7376d564ae0fd7aa0dc199c7cc875d719bbd7a8210c3af4ff9214c757be84c06408f43b61", < "sha256": "8d97b8e3377a2f17b7beb07b8a8c58933bde61c972d793ee6bc027ec3f5cbcfb", < "sha1": "ab91512edbc17ac7ef2d369b77906b2d5add9c60", < "md5": "3fe59873559f6d274e3ff7bc7481b233" --- > "size": 58333, > "sha512": "0225dfe4fc3230531784c422fe3c7c12f29e2dc753ef5c97e4872538eddbf402359cede34dbcf004de004a678d2aac4236b1c994d84b89815e2832bd4b5bdab2", > "sha256": "4aa1897f5bc88d7d59fd9045e7136bf025ad8136581362ee64734dfdfac294eb", > "sha1": "b9ae4d8b2a2800d42ce283385eb0274e4c7526c6", > "md5": "5b3e9280593bf22e248eb0ca0091ca1d" ``` --- build.gradle.kts | 7 +++++++ .../jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 0c1f8eb93..ea50a6192 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -98,6 +98,13 @@ publishing.publications.withType().configureEach { suppressPomMetadataWarningsFor(SOURCES_ELEMENTS_CONFIGURATION_NAME) } +configurations.named(API_ELEMENTS_CONFIGURATION_NAME) { + attributes.attribute( + GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, + objects.named("8.11"), + ) +} + val testGradleVersion: String = providers.gradleProperty("testGradleVersion").orNull.let { val value = if (it == null || it == "current") GradleVersion.current().version else it logger.lifecycle("Using Gradle $value in tests") diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 4ed334530..c1c4f22a4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.CONFIGURATION_NAME import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.EXTENSION_NAME -import org.gradle.api.GradleException import org.gradle.api.NamedDomainObjectProvider import org.gradle.api.Plugin import org.gradle.api.Project @@ -12,14 +11,10 @@ import org.gradle.api.attributes.Bundling import org.gradle.api.component.SoftwareComponentContainer import org.gradle.api.distribution.DistributionContainer import org.gradle.api.plugins.ExtensionContainer -import org.gradle.util.GradleVersion public abstract class ShadowBasePlugin : Plugin { override fun apply(project: Project): Unit = with(project) { - if (GradleVersion.current() < GradleVersion.version("8.11")) { - throw GradleException("This version of Shadow supports Gradle 8.11+ only. Please upgrade.") - } with(extensions.create(EXTENSION_NAME, ShadowExtension::class.java)) { addShadowVariantIntoJavaComponent.convention(true) addTargetJvmVersionAttribute.convention(true) From fc6ddff059cba246b081d150640fec4a43a83fa2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 9 Oct 2025 00:29:23 +0000 Subject: [PATCH 815/941] Update ASM and jdependency to support Java 26 (#1799) * Update dependency org.ow2.asm:asm-commons to v9.9 * Fix ctor for `Remapper` * org.vafer:jdependency:2.14 * Update changelog --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- docs/changes/README.md | 1 + gradle/libs.versions.toml | 4 ++-- .../gradle/plugins/shadow/internal/RelocatorRemapper.kt | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 5cd36e305..769d3fcff 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -6,6 +6,7 @@ ### Changed - Change the group of `startShadowScripts` from `application` to `other`. ([#1797](https://github.com/GradleUp/shadow/pull/1797)) +- Update ASM and jdependency to support Java 26. ([#1799](https://github.com/GradleUp/shadow/pull/1799)) ## [9.2.2](https://github.com/GradleUp/shadow/compare/9.2.2) - 2025-09-26 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0bc5a35b4..27152a79d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,9 +9,9 @@ apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsIo = "commons-io:commons-io:2.20.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.2" apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.11" -asm = "org.ow2.asm:asm-commons:9.8" +asm = "org.ow2.asm:asm-commons:9.9" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. -jdependency = "org.vafer:jdependency:2.13" +jdependency = "org.vafer:jdependency:2.14" jdom2 = "org.jdom:jdom2:2.0.6.1" kotlin-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version.ref = "kotlin" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index 27645ce27..ccf2bd659 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import com.github.jengelman.gradle.plugins.shadow.relocation.relocatePath import java.util.regex.Pattern +import org.objectweb.asm.Opcodes import org.objectweb.asm.commons.Remapper /** @@ -15,7 +16,7 @@ import org.objectweb.asm.commons.Remapper internal class RelocatorRemapper( private val relocators: Set, private val onModified: () -> Unit = {}, -) : Remapper() { +) : Remapper(Opcodes.ASM9) { override fun mapValue(value: Any): Any { return if (value is String) { From 3167eef3cccd743ebf74191b4f80d1452ff43abc Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 9 Oct 2025 10:38:43 +0800 Subject: [PATCH 816/941] Remove redundant log4j dependency for functionalTest (#1806) --- build.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index ea50a6192..35408cf71 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -173,7 +173,6 @@ testing.suites { implementation(libs.apache.maven.modelBuilder) implementation(libs.moshi) implementation(libs.moshi.kotlin) - implementation(libs.apache.log4j) } } From 88d03ea27f71350461344464b2559ea3a749bceb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 9 Oct 2025 11:19:34 +0800 Subject: [PATCH 817/941] Share gradleRunner (#1805) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- build.gradle.kts | 1 + .../shadow/snippet/SnippetExecutable.kt | 28 ++----- .../gradle/plugins/shadow/BasePluginTest.kt | 82 ++++++------------- .../shadow/util/AppendableMavenRepository.kt | 13 +-- .../plugins/shadow/testkit/GradleRunner.kt | 60 ++++++++++++++ 5 files changed, 100 insertions(+), 84 deletions(-) create mode 100644 src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt diff --git a/build.gradle.kts b/build.gradle.kts index 35408cf71..b25e3a3e8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -125,6 +125,7 @@ dependencies { implementation(libs.plexus.utils) implementation(libs.plexus.xml) + testKitImplementation(gradleTestKit()) testKitImplementation(libs.assertk) testPluginClasspath(libs.foojayResolver) diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index 00c9d6c5c..88a30614d 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -1,12 +1,14 @@ package com.github.jengelman.gradle.plugins.shadow.snippet +import com.github.jengelman.gradle.plugins.shadow.testkit.assertNoDeprecationWarnings +import com.github.jengelman.gradle.plugins.shadow.testkit.gradleRunner +import com.github.jengelman.gradle.plugins.shadow.testkit.toWarningsAsErrors import java.nio.file.Path import java.util.jar.JarOutputStream import kotlin.io.path.createDirectory import kotlin.io.path.createFile import kotlin.io.path.outputStream import kotlin.io.path.writeText -import org.gradle.testkit.runner.GradleRunner import org.junit.jupiter.api.function.Executable sealed class SnippetExecutable : Executable { @@ -86,24 +88,12 @@ sealed class SnippetExecutable : Executable { JarOutputStream(it.outputStream()).use {} } - val warningMode = if (mainScript.contains("org.jetbrains.kotlin.multiplatform")) { - "none" // TODO: https://youtrack.jetbrains.com/issue/KT-78620 - } else { - "fail" - } - try { - GradleRunner.create() - .withGradleVersion(testGradleVersion) - .withProjectDir(projectRoot.toFile()) - .withPluginClasspath() - .forwardOutput() - .withArguments( - "--warning-mode=$warningMode", - "--stacktrace", - "build", - ) - .build() + gradleRunner( + projectDir = projectRoot, + arguments = listOf("build", "--stacktrace"), + warningsAsErrors = mainScript.toWarningsAsErrors(), + ).build().assertNoDeprecationWarnings() } catch (t: Throwable) { throw RuntimeException("Failed to execute snippet:\n\n$mainScript", t) } @@ -131,8 +121,6 @@ sealed class SnippetExecutable : Executable { } companion object { - private val testGradleVersion = System.getProperty("TEST_GRADLE_VERSION") - ?: error("TEST_GRADLE_VERSION system property is not set.") private val lineSeparator = System.lineSeparator() 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 90ed09384..dca82cafc 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 @@ -2,8 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow import assertk.Assert import assertk.all -import assertk.assertThat -import assertk.assertions.doesNotContain import assertk.assertions.isEqualTo import assertk.assertions.isNotNull import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Companion.SHADOW_INSTALL_TASK_NAME @@ -11,7 +9,11 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowApplicationPlugin.Compan import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.testkit.JarPath +import com.github.jengelman.gradle.plugins.shadow.testkit.assertNoDeprecationWarnings +import com.github.jengelman.gradle.plugins.shadow.testkit.commonGradleArgs +import com.github.jengelman.gradle.plugins.shadow.testkit.gradleRunner import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath +import com.github.jengelman.gradle.plugins.shadow.testkit.toWarningsAsErrors import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder @@ -21,8 +23,6 @@ import java.nio.file.Path import java.util.Properties import java.util.jar.JarEntry import kotlin.io.path.ExperimentalPathApi -import kotlin.io.path.Path -import kotlin.io.path.absolutePathString import kotlin.io.path.appendText import kotlin.io.path.createDirectories import kotlin.io.path.createDirectory @@ -65,8 +65,7 @@ abstract class BasePluginTest { @BeforeAll fun beforeAll() { localRepo = AppendableMavenRepository( - createTempDirectory().resolve("local-maven-repo").createDirectories(), - runner(projectDir = null), + root = createTempDirectory().resolve("local-maven-repo").createDirectories(), ).apply { jarModule("junit", "junit", "3.8.2") { useJar(junitJar) @@ -205,17 +204,17 @@ abstract class BasePluginTest { fun run( vararg arguments: String, - runnerBlock: (GradleRunner) -> Unit = {}, + block: GradleRunner.() -> Unit = {}, ): BuildResult { - return runner(arguments = arguments.toList(), block = runnerBlock) + return runner(arguments = arguments.toList(), block = block) .build().assertNoDeprecationWarnings() } fun runWithFailure( vararg arguments: String, - runnerBlock: (GradleRunner) -> Unit = {}, + block: GradleRunner.() -> Unit = {}, ): BuildResult { - return runner(arguments = arguments.toList(), block = runnerBlock) + return runner(arguments = arguments.toList(), block = block) .buildAndFail().assertNoDeprecationWarnings() } @@ -379,52 +378,27 @@ abstract class BasePluginTest { ) } - fun runner( - arguments: Iterable = emptyList(), - projectDir: Path? = projectRoot, - block: (GradleRunner) -> Unit = {}, - ): GradleRunner = GradleRunner.create() - .withGradleVersion(testGradleVersion) - .forwardOutput() - .withPluginClasspath() - .withTestKitDir(testKitDir.toFile()) - .withArguments( - buildList { - val warningsAsErrors = try { - // TODO: https://youtrack.jetbrains.com/issue/KT-78620 - !projectScript.readText().contains("org.jetbrains.kotlin.multiplatform") - } catch (_: UninitializedPropertyAccessException) { - true // Default warning mode if projectScript is not initialized yet. - } - if (warningsAsErrors) { - add("--warning-mode=fail") - } - addAll(commonArguments) - addAll(arguments) - }, - ) - .apply { - if (projectDir != null) { - withProjectDir(projectDir.toFile()) - } - block(this) + private fun runner( + arguments: Iterable, + block: GradleRunner.() -> Unit, + ): GradleRunner { + val warningsAsErrors = try { + projectScript.readText().toWarningsAsErrors() + } catch (_: UninitializedPropertyAccessException) { + true // Default warning mode if projectScript is not initialized yet. } + return gradleRunner( + projectDir = projectRoot, + arguments = commonGradleArgs + arguments, + warningsAsErrors = warningsAsErrors, + block = block, + ) + } @Suppress("ConstPropertyName") companion object { - private val testGradleVersion = System.getProperty("TEST_GRADLE_VERSION") - ?: error("TEST_GRADLE_VERSION system property is not set.") - val lineSeparator: String = System.lineSeparator() - val testKitDir: Path = run { - var gradleUserHome = System.getenv("GRADLE_USER_HOME") - if (gradleUserHome == null) { - gradleUserHome = Path(System.getProperty("user.home"), ".gradle").absolutePathString() - } - Path(gradleUserHome, "testkit") - } - const val shadowPluginId = "com.gradleup.shadow" const val shadowJarPath = ":$SHADOW_JAR_TASK_NAME" const val serverShadowJarPath = ":server:$SHADOW_JAR_TASK_NAME" @@ -486,14 +460,6 @@ abstract class BasePluginTest { """.trimIndent() } - fun BuildResult.assertNoDeprecationWarnings() = apply { - assertThat(output).doesNotContain( - "has been deprecated and is scheduled to be removed in Gradle", - "has been deprecated. This is scheduled to be removed in Gradle", - "will fail with an error in Gradle", - ) - } - fun Assert.useAll(body: Assert.() -> Unit) = all { body() // Close the resource after all assertions are done. diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index 6d5e45d34..cda9c70c0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.util -import com.github.jengelman.gradle.plugins.shadow.BasePluginTest.Companion.commonArguments +import com.github.jengelman.gradle.plugins.shadow.testkit.commonGradleArgs +import com.github.jengelman.gradle.plugins.shadow.testkit.gradleRunner import java.nio.file.Path import kotlin.io.path.appendText import kotlin.io.path.createDirectory @@ -14,11 +15,9 @@ import kotlin.io.path.writeText import org.apache.maven.model.Dependency import org.apache.maven.model.Model import org.gradle.api.logging.Logging -import org.gradle.testkit.runner.GradleRunner class AppendableMavenRepository( val root: Path, - private val gradleRunner: GradleRunner, ) { private val modules = mutableListOf() private val jarsDir: Path @@ -71,9 +70,11 @@ class AppendableMavenRepository( } } - gradleRunner.withProjectDir(root.toFile()) - .withArguments(commonArguments + "publish") - .build() + gradleRunner( + projectDir = root, + arguments = commonGradleArgs + "publish", + ).build() + logger.info( """ Publish modules to Maven repository at ${root.toUri()}: diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt new file mode 100644 index 000000000..761f4522d --- /dev/null +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt @@ -0,0 +1,60 @@ +package com.github.jengelman.gradle.plugins.shadow.testkit + +import assertk.assertThat +import assertk.assertions.doesNotContain +import java.nio.file.Path +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.GradleRunner + +private val testKitDir by lazy { + val gradleUserHome = System.getenv("GRADLE_USER_HOME") + ?: Path(System.getProperty("user.home"), ".gradle").absolutePathString() + Path(gradleUserHome, "testkit") +} + +private val testGradleVersion by lazy { + System.getProperty("TEST_GRADLE_VERSION") ?: error("TEST_GRADLE_VERSION system property is not set.") +} + +val commonGradleArgs = setOf( + "--configuration-cache", + "--build-cache", + "--parallel", + "--stacktrace", + // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel + "-Dorg.gradle.configuration-cache.parallel=true", +) + +fun gradleRunner( + projectDir: Path, + arguments: Iterable, + warningsAsErrors: Boolean = true, + block: GradleRunner.() -> Unit = {}, +): GradleRunner = GradleRunner.create() + .withGradleVersion(testGradleVersion) + .forwardOutput() + .withPluginClasspath() + .withTestKitDir(testKitDir.toFile()) + .withArguments( + buildList { + addAll(arguments) + if (warningsAsErrors) { + add("--warning-mode=fail") + } + }, + ) + .withProjectDir(projectDir.toFile()) + .apply(block) + +fun BuildResult.assertNoDeprecationWarnings() = apply { + assertThat(output).doesNotContain( + "has been deprecated and is scheduled to be removed in Gradle", + "has been deprecated. This is scheduled to be removed in Gradle", + "will fail with an error in Gradle", + ) +} + +// TODO: https://youtrack.jetbrains.com/issue/KT-78620 +fun String.toWarningsAsErrors(): Boolean = !contains("org.jetbrains.kotlin.multiplatform") From 624a4cee176b19cc95dc432f6c2296707eb44c46 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 9 Oct 2025 11:20:00 +0800 Subject: [PATCH 818/941] Rename run to runWithSuccess (#1807) --- .../plugins/shadow/ApplicationPluginTest.kt | 18 +++--- .../gradle/plugins/shadow/BasePluginTest.kt | 2 +- .../gradle/plugins/shadow/CachingTest.kt | 4 +- .../gradle/plugins/shadow/FilteringTest.kt | 20 +++---- .../gradle/plugins/shadow/GroovyPluginTest.kt | 2 +- .../gradle/plugins/shadow/JavaPluginsTest.kt | 60 +++++++++---------- .../plugins/shadow/KotlinPluginsTest.kt | 10 ++-- .../gradle/plugins/shadow/MinimizeTest.kt | 22 +++---- .../gradle/plugins/shadow/PublishingTest.kt | 2 +- .../gradle/plugins/shadow/RelocationTest.kt | 32 +++++----- .../gradle/plugins/shadow/ScalaPluginTest.kt | 2 +- .../transformers/AppendingTransformerTest.kt | 4 +- .../GroovyExtensionModuleTransformerTest.kt | 6 +- .../PropertiesFileTransformerTest.kt | 10 ++-- .../ServiceFileTransformerTest.kt | 16 ++--- .../shadow/transformers/TransformersTest.kt | 18 +++--- .../XmlAppendingTransformerTest.kt | 4 +- 17 files changed, 116 insertions(+), 116 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 96d56543f..f7fb2a4c5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -60,7 +60,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - val result = run(runShadowPath) + val result = runWithSuccess(runShadowPath) assertThat(result.output).contains( "Running application with JDK 17", @@ -77,7 +77,7 @@ class ApplicationPluginTest : BasePluginTest() { applicationBlock = "applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED']", ) - run(installShadowDistPath) + runWithSuccess(installShadowDistPath) val installPath = path("build/install/") assertThat(installPath.walkEntries()).containsOnly( @@ -119,7 +119,7 @@ class ApplicationPluginTest : BasePluginTest() { fun installShadowDoesNotExecuteDependentShadowTask() { prepare() - run(installShadowDistPath) + runWithSuccess(installShadowDistPath) commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) } @@ -140,7 +140,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - var result = run(runShadowPath) + var result = runWithSuccess(runShadowPath) val assertions = { output: String, arg: String -> assertThat(output).all { @@ -164,7 +164,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - result = run(":run") + result = runWithSuccess(":run") assertions(result.output, "bar") } @@ -180,7 +180,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - run(runShadowPath) // Run without errors. + runWithSuccess(runShadowPath) // Run without errors. assertThat(jarPath("build/libs/myapp-1.0-all.jar")).useAll { getMainAttr(mainClassAttributeKey).isEqualTo("my.Main2") @@ -217,7 +217,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowDistZipPath) + runWithSuccess(shadowDistZipPath) val zipPath = path("build/distributions/myapp-shadow-1.0.zip") ZipFile(zipPath.toFile()).use { zip -> @@ -241,7 +241,7 @@ class ApplicationPluginTest : BasePluginTest() { path("src/dist/echo.sh").writeText("echo 'Hello, World!'") prepare() - run(shadowDistZipPath) + runWithSuccess(shadowDistZipPath) val zipPath = path("build/distributions/myapp-shadow-1.0.zip") ZipFile(zipPath.toFile()).use { zip -> @@ -269,7 +269,7 @@ class ApplicationPluginTest : BasePluginTest() { """.trimIndent(), ) - run(installShadowDistPath, shadowDistZipPath) + runWithSuccess(installShadowDistPath, shadowDistZipPath) assertThat(path("build/install/").walkEntries()).containsOnly( "${applicationNames.second}-shadow/${executableDirs.second}/${applicationNames.second}", 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 dca82cafc..96a9531a9 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 @@ -202,7 +202,7 @@ abstract class BasePluginTest { return JarBuilder(path("temp/$relative")).apply(builder).write() } - fun run( + fun runWithSuccess( vararg arguments: String, block: GradleRunner.() -> Unit = {}, ): BuildResult { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt index 3f4843d6c..eee97ff53 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -514,7 +514,7 @@ class CachingTest : BasePluginTest() { } private fun cleanOutputs() { - run("clean") + runWithSuccess("clean") @OptIn(ExperimentalPathApi::class) val buildDirs = projectRoot.walk().filter { it.isDirectory() && it.name == "build" } // Make sure build folders are deleted by clean task. @@ -552,7 +552,7 @@ class CachingTest : BasePluginTest() { } private fun assertRunWithResult(expectedOutcome: TaskOutcome) { - val result = run(taskPath) + val result = runWithSuccess(taskPath) assertThat(result).taskOutcomeEquals(taskPath, expectedOutcome) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 04aa43ede..612522f68 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -26,7 +26,7 @@ class FilteringTest : BasePluginTest() { @Test fun includeAllDependencies() { - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -46,7 +46,7 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -83,7 +83,7 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) commonAssertions() } @@ -113,7 +113,7 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) commonAssertions() } @@ -139,7 +139,7 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -163,7 +163,7 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsOnly( @@ -189,7 +189,7 @@ class FilteringTest : BasePluginTest() { ) path("client/build.gradle").appendText("version = '1.0.0+1'") - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsOnly( @@ -211,7 +211,7 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsOnly( @@ -236,7 +236,7 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -263,7 +263,7 @@ class FilteringTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt index e5b1061fa..28bfa7e77 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt @@ -27,7 +27,7 @@ class GroovyPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 334fd14e7..faece7ecc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -80,7 +80,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = run(ASSEMBLE_TASK_NAME) + val result = runWithSuccess(ASSEMBLE_TASK_NAME) assertThat(result.task(":$ASSEMBLE_TASK_NAME")).isNotNull() .transform { it.outcome }.isEqualTo(SUCCESS) @@ -93,7 +93,7 @@ class JavaPluginsTest : BasePluginTest() { @Test fun shadowJarCliOptions() { - val result = run("help", "--task", shadowJarPath) + val result = runWithSuccess("help", "--task", shadowJarPath) assertThat(result.output).contains( "--add-multi-release-attribute Adds the multi-release attribute to the manifest if any dependencies contain it.", @@ -113,7 +113,7 @@ class JavaPluginsTest : BasePluginTest() { fun includeProjectDependencies() { writeClientAndServerModules() - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsOnly( @@ -131,7 +131,7 @@ class JavaPluginsTest : BasePluginTest() { fun dependOnProjectShadowJar() { writeClientAndServerModules(clientShadowed = true) - run(":server:jar") + runWithSuccess(":server:jar") assertThat(jarPath("server/build/libs/server-1.0.jar")).useAll { containsOnly( @@ -158,7 +158,7 @@ class JavaPluginsTest : BasePluginTest() { val relocatedEntries = junitEntries .map { it.replace("junit/framework/", "client/junit/framework/") }.toTypedArray() - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsOnly( @@ -212,7 +212,7 @@ class JavaPluginsTest : BasePluginTest() { ) path("client/src/custom/resources/Foo.bar").writeText("Foo=Bar") - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsOnly( @@ -253,7 +253,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = run(serverShadowJarPath, infoArgument) + val result = runWithSuccess(serverShadowJarPath, infoArgument) assertThat(result.output).contains( if (addAttribute) { @@ -281,7 +281,7 @@ class JavaPluginsTest : BasePluginTest() { ) val arg = if (enable) "--add-multi-release-attribute" else "--no-add-multi-release-attribute" - val result = run(serverShadowJarPath, infoArgument, arg) + val result = runWithSuccess(serverShadowJarPath, infoArgument, arg) assertThat(result.output).contains( if (enable) { @@ -319,7 +319,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -341,7 +341,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -383,7 +383,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -406,7 +406,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { transform { it.mainAttrSize }.isEqualTo(1) @@ -433,7 +433,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val actual = outputShadowedJar.use { it.getMainAttr(classPathAttributeKey) } val expected = when (configuration) { @@ -456,7 +456,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val value = outputShadowedJar.use { it.getMainAttr(classPathAttributeKey) } assertThat(value).isEqualTo("junit-3.8.2.jar") @@ -480,7 +480,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -506,7 +506,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { transform { actual -> actual.entries().toList().map { it.name }.filter { it.endsWith(".class") } } @@ -584,7 +584,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(testShadowJarTask) + runWithSuccess(testShadowJarTask) assertThat(jarPath("build/libs/my-1.0-test.jar")).useAll { containsOnly( @@ -635,7 +635,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = run(testShadowJarTask) + val result = runWithSuccess(testShadowJarTask) assertThat(result.output).contains( "Has ShadowPlugin: false", @@ -681,7 +681,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run("jar", dependencyShadowJar) + runWithSuccess("jar", dependencyShadowJar) assertThat(jarPath("build/libs/my-1.0.jar")).useAll { containsOnly( @@ -762,7 +762,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -803,7 +803,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -839,7 +839,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -889,7 +889,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly(*entriesInAB, *manifestEntries) @@ -908,7 +908,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = run( + val result = runWithSuccess( serverShadowJarPath, ipArgument, infoArgument, @@ -944,7 +944,7 @@ class JavaPluginsTest : BasePluginTest() { val result = if (enable) { runWithFailure(shadowJarPath) } else { - run(shadowJarPath, infoArgument) + runWithSuccess(shadowJarPath, infoArgument) } assertThat(result.output).contains( @@ -971,7 +971,7 @@ class JavaPluginsTest : BasePluginTest() { val result = if (enable) { runWithFailure(shadowJarPath, "--fail-on-duplicate-entries") } else { - run(shadowJarPath, infoArgument, "--no-fail-on-duplicate-entries") + runWithSuccess(shadowJarPath, infoArgument, "--no-fail-on-duplicate-entries") } assertThat(result.output).contains( @@ -991,7 +991,7 @@ class JavaPluginsTest : BasePluginTest() { """.trimIndent(), ) - val result = run(shadowJarPath, infoArgument) + val result = runWithSuccess(shadowJarPath, infoArgument) assertThat(result.output).contains( message, @@ -1005,9 +1005,9 @@ class JavaPluginsTest : BasePluginTest() { @MethodSource("fallbackMainClassProvider") fun fallbackMainClassByCliOption(input: String, expected: String?) { if (input.isEmpty()) { - run(shadowJarPath) + runWithSuccess(shadowJarPath) } else { - run(shadowJarPath, "--main-class", input) + runWithSuccess(shadowJarPath, "--main-class", input) } assertThat(outputShadowedJar).useAll { @@ -1016,7 +1016,7 @@ class JavaPluginsTest : BasePluginTest() { } private fun dependencies(configuration: String, vararg flags: String): String { - return run("dependencies", "--configuration", configuration, *flags).output + return runWithSuccess("dependencies", "--configuration", configuration, *flags).output } private companion object { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 3b3c7c1e8..6a13603d1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -40,7 +40,7 @@ class KotlinPluginsTest : BasePluginTest() { ) val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Kotlin) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { val entries = arrayOf( @@ -85,7 +85,7 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { val entries = arrayOf( @@ -133,7 +133,7 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { val entries = arrayOf( @@ -188,7 +188,7 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { getMainAttr(mainClassAttributeKey).isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) @@ -219,7 +219,7 @@ class KotlinPluginsTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { getMainAttr(mainClassAttributeKey).isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index 57834736e..272ee76fd 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -25,7 +25,7 @@ class MinimizeTest : BasePluginTest() { fun useMinimizeWithDependenciesWithApiScope() { writeApiLibAndImplModules() - run(":impl:$SHADOW_JAR_TASK_NAME") + runWithSuccess(":impl:$SHADOW_JAR_TASK_NAME") assertThat(outputImplShadowedJar).useAll { containsAtLeast( @@ -59,7 +59,7 @@ class MinimizeTest : BasePluginTest() { """.trimIndent(), ) - run(":impl:$SHADOW_JAR_TASK_NAME") + runWithSuccess(":impl:$SHADOW_JAR_TASK_NAME") assertThat(outputImplShadowedJar).useAll { containsOnly( @@ -98,7 +98,7 @@ class MinimizeTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsAtLeast( @@ -126,7 +126,7 @@ class MinimizeTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsAtLeast( @@ -156,7 +156,7 @@ class MinimizeTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsOnly( @@ -193,7 +193,7 @@ class MinimizeTest : BasePluginTest() { """.trimIndent(), ) - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsAtLeast( @@ -209,7 +209,7 @@ class MinimizeTest : BasePluginTest() { public class Client {} """.trimIndent(), ) - run(serverShadowJarPath) + runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { containsAtLeast( @@ -239,7 +239,7 @@ class MinimizeTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -256,9 +256,9 @@ class MinimizeTest : BasePluginTest() { writeClientAndServerModules() if (enable) { - run(serverShadowJarPath, "--minimize-jar") + runWithSuccess(serverShadowJarPath, "--minimize-jar") } else { - run(serverShadowJarPath, "--no-minimize-jar") + runWithSuccess(serverShadowJarPath, "--no-minimize-jar") } assertThat(outputServerShadowedJar).useAll { @@ -297,7 +297,7 @@ class MinimizeTest : BasePluginTest() { """.trimIndent(), ) - run(":impl:$SHADOW_JAR_TASK_NAME") + runWithSuccess(":impl:$SHADOW_JAR_TASK_NAME") assertThat(outputImplShadowedJar).useAll { containsAtLeast( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index c7c31371d..3bb6bb997 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -612,7 +612,7 @@ class PublishingTest : BasePluginTest() { return JarPath(remoteRepoPath.resolve(relative)) } - private fun publish(vararg arguments: String): BuildResult = run("publish", *arguments) + private fun publish(vararg arguments: String): BuildResult = runWithSuccess("publish", *arguments) private fun publishConfiguration( projectBlock: String = "", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 668fccd2e..d3aef137c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -60,7 +60,7 @@ class RelocationTest : BasePluginTest() { } }.toTypedArray() - val result = run(shadowJarPath, infoArgument) + val result = runWithSuccess(shadowJarPath, infoArgument) assertThat(outputShadowedJar).useAll { containsOnly( @@ -92,9 +92,9 @@ class RelocationTest : BasePluginTest() { .toTypedArray() if (enable) { - run(shadowJarPath, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") + runWithSuccess(shadowJarPath, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") } else { - run(shadowJarPath, "--no-enable-auto-relocation", "--relocation-prefix=$relocationPrefix") + runWithSuccess(shadowJarPath, "--no-enable-auto-relocation", "--relocation-prefix=$relocationPrefix") } val commonEntries = arrayOf( @@ -145,7 +145,7 @@ class RelocationTest : BasePluginTest() { .map { it.replace("junit/framework/", "b/") }.toTypedArray() val otherJunitEntries = junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -187,7 +187,7 @@ class RelocationTest : BasePluginTest() { .map { it.replace("junit/framework/", "b/") }.toTypedArray() val otherJunitEntries = junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -234,7 +234,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -280,7 +280,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -312,7 +312,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) if (enableAutoRelocation) { val (relocatedEntries, otherEntries) = outputShadowedJar.use { @@ -399,7 +399,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -434,7 +434,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { if (exclude) { @@ -467,7 +467,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -494,7 +494,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) assertThat(result).contains( @@ -524,7 +524,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) if (skipStringConstants) { @@ -568,7 +568,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) // Just check that the jar can be executed without NoClassDefFoundError. @@ -593,7 +593,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(":jar", shadowJarPath) + runWithSuccess(":jar", shadowJarPath) val originalBytes = outputJar.use { it.getBytes(mainClassEntry) } val relocatedBytes = outputShadowedJar.use { it.getBytes(mainClassEntry) } @@ -621,7 +621,7 @@ class RelocationTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val relocatedModuleFilePath = "META-INF/kotlin-stdlib.shadow.kotlin_module" assertThat(outputShadowedJar).useAll { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt index ed3286d84..6046d6a0b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt @@ -27,7 +27,7 @@ class ScalaPluginTest : BasePluginTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index a770165b1..8d417f309 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -36,7 +36,7 @@ class AppendingTransformerTest : BaseTransformerTest() { } projectScript.appendText(config) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent(ENTRY_TEST_PROPERTIES) } assertThat(content).isEqualTo(CONTENT_ONE_TWO) @@ -83,7 +83,7 @@ class AppendingTransformerTest : BaseTransformerTest() { projectScript.appendText(config) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val content1 = outputShadowedJar.use { it.getContent("resources/$APPLICATION_YML_FILE") } assertThat(content1).isEqualTo( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index 6b717adce..4dbbaf736 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -40,7 +40,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { } projectScript.appendText(config) - run(shadowJarPath) + runWithSuccess(shadowJarPath) commonAssertions() } @@ -60,7 +60,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) commonAssertions() } @@ -79,7 +79,7 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val properties = outputShadowedJar.extensionModuleProperties diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index 525f7174f..b12c96175 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -33,7 +33,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val expected = when (strategy) { MergeStrategy.First -> arrayOf("key1=one", "key2=one", "key3=two") @@ -63,7 +63,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } assertThat(content).contains("FOO=bar,baz") @@ -88,7 +88,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent("META-INF/utf8.properties") } assertThat(content).contains("foo=第一,第二") @@ -116,7 +116,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { getContent("META-INF/foo.properties").contains("foo=1;3") @@ -146,7 +146,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } assertThat(content.trimIndent()).isEqualTo( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index a80d4af57..6162f3f3d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -44,7 +44,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } projectScript.appendText(config) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) @@ -80,7 +80,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } projectScript.appendText(config) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent(ENTRY_FOO_SHADE) } assertThat(content).isEqualTo(CONTENT_ONE_TWO) @@ -138,7 +138,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { getContent("META-INF/services/java.sql.Driver").isEqualTo( @@ -189,7 +189,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { ) path("src/main/resources/$servicesBarEntry").writeText(CONTENT_THREE) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent(servicesBarEntry) } assertThat(content).isEqualTo(CONTENT_THREE + "\n" + CONTENT_ONE_TWO) @@ -217,7 +217,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { ) { writeDuplicatesStrategy(strategy) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(firstValue) @@ -238,7 +238,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) @@ -259,7 +259,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) @@ -287,7 +287,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { getContent(ENTRY_SERVICES_SHADE).isEqualTo(CONTENT_ONE_TWO) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 37224074a..69bb88224 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -38,7 +38,7 @@ class TransformersTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) commonAssertions { assertThat(getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("PASSED") @@ -52,7 +52,7 @@ class TransformersTest : BaseTransformerTest() { projectScript.appendText(MANIFEST_ATTRS) - run(shadowJarPath) + runWithSuccess(shadowJarPath) commonAssertions() } @@ -75,7 +75,7 @@ class TransformersTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val actualFileBytes = outputShadowedJar.use { jar -> jar.getStream(PLUGIN_CACHE_FILE).use { it.readAllBytes() } @@ -99,7 +99,7 @@ class TransformersTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -123,7 +123,7 @@ class TransformersTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -153,7 +153,7 @@ class TransformersTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -182,7 +182,7 @@ class TransformersTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -229,7 +229,7 @@ class TransformersTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsOnly( @@ -267,7 +267,7 @@ class TransformersTest : BaseTransformerTest() { """.trimIndent(), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { containsAtLeast(*entriesInAB) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index 446a9f5c5..2cc226f20 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -34,7 +34,7 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent(xmlEntry) }.trimIndent() assertThat(content).isEqualTo( @@ -75,7 +75,7 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { ), ) - run(shadowJarPath) + runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent(xmlEntry) }.trimIndent() assertThat(content).isEqualTo( From 388d35d847f7833f00bc1406b7baf042c7e06697 Mon Sep 17 00:00:00 2001 From: Goooler Date: Thu, 9 Oct 2025 12:07:32 +0800 Subject: [PATCH 819/941] Update compatibility matrix --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index 5bd0aa91a..a983dd012 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,6 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. | Shadow Version | Min Gradle Version | Min Java Version | Plugin ID | |----------------|--------------------|------------------|------------------------------------------------------| -| 5.2.0 - 6.1.0 | 5.x - 6.x | 7 | [`com.github.johnrengelman.shadow`][johnrengelman's] | -| 6.1.0+ | 6.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | -| 7.0.0+ | 7.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.0.0+ | 8.0 | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | | 9.0.0+ | 8.11 | 11 | [`com.gradleup.shadow`][gradleup's] | From 3e44233862cea807d3a075005d0c72e55dee8b6c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 9 Oct 2025 12:13:40 +0800 Subject: [PATCH 820/941] Bump min Gradle requirement to 9.0.0 (#1801) --- .github/workflows/build.yml | 8 ++------ README.md | 1 + build.gradle.kts | 4 ++-- docs/README.md | 1 + docs/changes/README.md | 1 + .../gradle/plugins/shadow/ApplicationPluginTest.kt | 8 ++++---- .../jengelman/gradle/plugins/shadow/JavaPluginsTest.kt | 10 +++++----- .../plugins/shadow/relocation/SimpleRelocatorTest.kt | 8 ++++---- 8 files changed, 20 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 23231a572..7a0356567 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,15 +16,11 @@ jobs: # Always test on the latest version and some LTS. java: [ 17, 24 ] # Test on the minimum Gradle version and the latest. - gradle: [ 8.11, current ] - exclude: - # Gradle 8.11 doesn't support Java 24. - - gradle: 8.11 - java: 24 + gradle: [ 9.0.0, current ] include: # We just test one JDK version on Windows. - os: windows-11-arm - gradle: 8.11 + gradle: 9.0.0 java: 21 - os: windows-11-arm gradle: current diff --git a/README.md b/README.md index a983dd012..787b9a6e4 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ Gradle plugin for creating fat/uber JARs with support for package relocation. | 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | | 9.0.0+ | 8.11 | 11 | [`com.gradleup.shadow`][gradleup's] | | 9.2.0+ | 8.11 | 17 | [`com.gradleup.shadow`][gradleup's] | +| 9.3.0+ | 9.0 | 17 | [`com.gradleup.shadow`][gradleup's] | diff --git a/build.gradle.kts b/build.gradle.kts index b25e3a3e8..10d5e2889 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -37,7 +37,7 @@ kotlin { compilerOptions { allWarningsAsErrors = true // https://docs.gradle.org/current/userguide/compatibility.html#kotlin - apiVersion = KotlinVersion.KOTLIN_2_0 // TODO: upgrade to 2.2 when the min Gradle requirement is 9.0+ + apiVersion = KotlinVersion.KOTLIN_2_2 languageVersion = apiVersion jvmTarget = JvmTarget.fromTarget(libs.versions.jdkRelease.get()) jvmDefault = JvmDefaultMode.NO_COMPATIBILITY @@ -101,7 +101,7 @@ publishing.publications.withType().configureEach { configurations.named(API_ELEMENTS_CONFIGURATION_NAME) { attributes.attribute( GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, - objects.named("8.11"), + objects.named("9.0.0"), ) } diff --git a/docs/README.md b/docs/README.md index dd7b0e6be..39ff69f74 100644 --- a/docs/README.md +++ b/docs/README.md @@ -29,6 +29,7 @@ dependent libraries into the output jar without incurring the I/O overhead of ex | 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | | 9.0.0+ | 8.11 | 11 | [`com.gradleup.shadow`][gradleup's] | | 9.2.0+ | 8.11 | 17 | [`com.gradleup.shadow`][gradleup's] | +| 9.3.0+ | 9.0 | 17 | [`com.gradleup.shadow`][gradleup's] | ## Benefits of Shadow diff --git a/docs/changes/README.md b/docs/changes/README.md index 769d3fcff..e6ac914f2 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,6 +7,7 @@ - Change the group of `startShadowScripts` from `application` to `other`. ([#1797](https://github.com/GradleUp/shadow/pull/1797)) - Update ASM and jdependency to support Java 26. ([#1799](https://github.com/GradleUp/shadow/pull/1799)) +- Bump min Gradle requirement to 9.0.0. ([#1801](https://github.com/GradleUp/shadow/pull/1801)) ## [9.2.2](https://github.com/GradleUp/shadow/compare/9.2.2) - 2025-09-26 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index f7fb2a4c5..e4335374b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -53,9 +53,9 @@ class ApplicationPluginTest : BasePluginTest() { id 'org.gradle.toolchains.foojay-resolver-convention' } """.trimIndent(), - runShadowBlock = """ + runShadowBlock = $$""" doFirst { - logger.lifecycle("Running application with JDK ${'$'}{it.javaLauncher.get().metadata.languageVersion.asInt()}") + logger.lifecycle("Running application with JDK ${it.javaLauncher.get().metadata.languageVersion.asInt()}") } """.trimIndent(), ) @@ -95,8 +95,8 @@ class ApplicationPluginTest : BasePluginTest() { val winScript = path("myapp-shadow/bin/myapp.bat", installPath) assertThat(unixScript.readText()).contains( - "CLASSPATH=\$APP_HOME/lib/myapp-1.0-all.jar", - "exec \"\$JAVACMD\" \"$@\"", + $$"CLASSPATH=$APP_HOME/lib/myapp-1.0-all.jar", + $$"exec \"$JAVACMD\" \"$@\"", "DEFAULT_JVM_OPTS='\"--add-opens=java.base/java.lang=ALL-UNNAMED\"'", ) assertThat(winScript.readText()).contains( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index faece7ecc..ce55527e3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -863,12 +863,12 @@ class JavaPluginsTest : BasePluginTest() { fun includeFilesInTaskOutputDirectory() { // Create a build that has a task with jars in the output directory projectScript.appendText( - """ + $$""" def createJars = tasks.register('createJars') { - def artifactAJar = file('${artifactAJar.invariantSeparatorsPathString}') - def artifactBJar = file('${artifactBJar.invariantSeparatorsPathString}') + def artifactAJar = file('$${artifactAJar.invariantSeparatorsPathString}') + def artifactBJar = file('$${artifactBJar.invariantSeparatorsPathString}') inputs.files(artifactAJar, artifactBJar) - def outputDir = file('${'$'}{buildDir}/jars') + def outputDir = file('${buildDir}/jars') outputs.dir(outputDir) doLast { artifactAJar.withInputStream { input -> @@ -883,7 +883,7 @@ class JavaPluginsTest : BasePluginTest() { } } } - $shadowJarTask { + $$shadowJarTask { includedDependencies.from(files(createJars).asFileTree) } """.trimIndent(), diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 43374d6ef..743c6ff84 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -81,13 +81,13 @@ class SimpleRelocatorTest { includes = listOf("%regex[org/foo/R(\\$.*)?$]"), ) assertThat(relocator.canRelocatePath("org/foo/R.class")).isTrue() - assertThat(relocator.canRelocatePath("org/foo/R\$string.class")).isTrue() - assertThat(relocator.canRelocatePath("org/foo/R\$layout.class")).isTrue() + assertThat(relocator.canRelocatePath($$"org/foo/R$string.class")).isTrue() + assertThat(relocator.canRelocatePath($$"org/foo/R$layout.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/Recording/R.class")).isFalse() assertThat(relocator.canRelocatePath("org/foo/Recording.class")).isFalse() - assertThat(relocator.canRelocatePath("org/foo/bar/R\$string.class")).isFalse() + assertThat(relocator.canRelocatePath($$"org/foo/bar/R$string.class")).isFalse() assertThat(relocator.canRelocatePath("org/R.class")).isFalse() - assertThat(relocator.canRelocatePath("org/R\$string.class")).isFalse() + assertThat(relocator.canRelocatePath($$"org/R$string.class")).isFalse() // Exclude with Regex relocator = SimpleRelocator( From 437ab4598c3d4a2984d4daee4e6fdd8fd4349291 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 10 Oct 2025 15:46:13 +0800 Subject: [PATCH 821/941] Remove commonArguments --- .../jengelman/gradle/plugins/shadow/BasePluginTest.kt | 9 --------- 1 file changed, 9 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 96a9531a9..4644a3d91 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 @@ -424,15 +424,6 @@ abstract class BasePluginTest { const val runShadowTask = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)" const val jarTask = "tasks.named('jar', Jar)" - val commonArguments = listOf( - "--configuration-cache", - "--build-cache", - "--parallel", - "--stacktrace", - // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel - "-Dorg.gradle.configuration-cache.parallel=true", - ) - // TODO: enable this flag for all tests once we have fixed all issues with isolated projects. // See https://github.com/GradleUp/shadow/pull/1139. const val ipArgument = "-Dorg.gradle.unsafe.isolated-projects=true" From f4e9a92b69677b82b5dd9b915f3a42261e5a1429 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 14 Oct 2025 08:15:54 +0800 Subject: [PATCH 822/941] Update Develocity to v4.2.2 (#1815) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 27152a79d..e4406170a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ 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:1.0.0" -develocity = "com.gradle:develocity-gradle-plugin:4.2.1" +develocity = "com.gradle:develocity-gradle-plugin:4.2.2" kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } diff --git a/settings.gradle.kts b/settings.gradle.kts index d6d9cb882..d172b492c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "4.2.1" + id("com.gradle.develocity") version "4.2.2" } develocity { From ff07485a75ddf77699ccf2c58cfd04e82b88496c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Oct 2025 10:46:20 +0000 Subject: [PATCH 823/941] Update plugin jetbrains-dokka to v2.1.0 (#1817) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e4406170a..922249323 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,7 +36,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.13.0" -jetbrains-dokka = "org.jetbrains.dokka:2.1.0-Beta" +jetbrains-dokka = "org.jetbrains.dokka:2.1.0" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } spotless = "com.diffplug.spotless:8.0.0" From 4d6d87fbfac76e70c990c30dae29c60c458a1080 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 14:19:38 +0000 Subject: [PATCH 824/941] Update Gradle to v9.2.0-rc-2 (#1818) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 45457 -> 45633 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 8bdaf60c75ab801e22807dde59e12a8735a34077..f8e1ee3125fe0768e9a76ee977ac089eb657005e 100644 GIT binary patch delta 36466 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 delta 36287 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 TbTtHgKIb=}#OPPBcmMwnM9LS( diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 901ca4ae0..dfc438b04 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-rc-1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-rc-2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 7dec2519dd895d03c2953dd7ec6e01e5e5185b96 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 22 Oct 2025 12:10:35 +0800 Subject: [PATCH 825/941] Always test on the latest Java (#1819) --- .github/renovate.json5 | 20 ++++++++++++++++++++ .github/workflows/.java-version | 1 + .github/workflows/build.yml | 20 +++++--------------- .github/workflows/release.yml | 2 +- 4 files changed, 27 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/.java-version diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 5a9df2721..ef52f4023 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -22,5 +22,25 @@ "com.gradle.develocity:com.gradle.develocity.gradle.plugin", ], }, + ], + ignorePresets: [ + // Ensure we get the latest version and are not pinned to old versions. + 'workarounds:javaLTSVersions', + ], + customManagers: [ + // Update .java-version file with the latest JDK version. + { + customType: 'regex', + fileMatch: [ + '\\.java-version$', + ], + matchStrings: [ + '(?.*)\\n', + ], + datasourceTemplate: 'java-version', + depNameTemplate: 'java', + // Only write the major version. + extractVersionTemplate: '^(?\\d+)', + }, ] } diff --git a/.github/workflows/.java-version b/.github/workflows/.java-version new file mode 100644 index 000000000..a45fd52cc --- /dev/null +++ b/.github/workflows/.java-version @@ -0,0 +1 @@ +24 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a0356567..9c14a5690 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,29 +9,19 @@ on: jobs: build: - name: OS=${{ matrix.os }}, Java=${{ matrix.java }}, Gradle=${{ matrix.gradle }} + name: OS=${{ matrix.os }}, Gradle=${{ matrix.gradle }} strategy: matrix: - os: [ ubuntu-24.04-arm ] - # Always test on the latest version and some LTS. - java: [ 17, 24 ] + os: [ ubuntu-24.04-arm, windows-latest ] # Test on the minimum Gradle version and the latest. gradle: [ 9.0.0, current ] - include: - # We just test one JDK version on Windows. - - os: windows-11-arm - gradle: 9.0.0 - java: 21 - - os: windows-11-arm - gradle: current - java: 21 runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v5 - uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: ${{ matrix.java }} + java-version-file: .github/workflows/.java-version - uses: gradle/actions/setup-gradle@v5 - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" --stacktrace -x lint @@ -43,7 +33,7 @@ jobs: - uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: 21 + java-version-file: .github/workflows/.java-version - uses: gradle/actions/setup-gradle@v5 - run: ./gradlew lint @@ -73,7 +63,7 @@ jobs: - uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: 21 + java-version-file: .github/workflows/.java-version - uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6e866e8f1..f59796204 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/setup-java@v5 with: distribution: 'zulu' - java-version: 21 + java-version-file: .github/workflows/.java-version - uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true From fe96f83548dbbff9afd6640de9dd2d0fe8be08b1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 22 Oct 2025 12:24:07 +0800 Subject: [PATCH 826/941] Revert "Add explicit lint workflow" (#1820) This reverts commit 9de3b215 --- .github/workflows/build.yml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9c14a5690..fbe655472 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,25 +23,12 @@ jobs: distribution: 'zulu' java-version-file: .github/workflows/.java-version - uses: gradle/actions/setup-gradle@v5 - - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" --stacktrace -x lint - - # TODO: https://issuetracker.google.com/issues/438361087 - lint: - runs-on: ubuntu-24.04-arm - steps: - - uses: actions/checkout@v5 - - uses: actions/setup-java@v5 - with: - distribution: 'zulu' - java-version-file: .github/workflows/.java-version - - uses: gradle/actions/setup-gradle@v5 - - run: ./gradlew lint + - run: ./gradlew build "-PtestGradleVersion=${{ matrix.gradle }}" --stacktrace # Status check that is required in branch protection rules. build-status: needs: - build - - lint runs-on: ubuntu-24.04-arm if: always() steps: From 4fe08ba027ab406b50ea89cb8e3822ba0c9b9dc7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 23 Oct 2025 15:31:11 +0800 Subject: [PATCH 827/941] Kotlin 2.3.0-Beta1 (#1808) --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 922249323..ee665413a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] jdkRelease = "17" -kotlin = "2.2.20" +kotlin = "2.3.0-Beta1" moshi = "1.15.2" pluginPublish = "2.0.0" From dca441f6fa7bfdeca247b1220293dce46f643f67 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 24 Oct 2025 15:23:42 +0800 Subject: [PATCH 828/941] Tidy up the usage of GradlePluginApiVersion (#1822) --- build.gradle.kts | 3 ++- gradle/libs.versions.toml | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 10d5e2889..160a7203d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -100,8 +100,9 @@ publishing.publications.withType().configureEach { configurations.named(API_ELEMENTS_CONFIGURATION_NAME) { attributes.attribute( + // TODO: https://github.com/gradle/gradle/issues/24608 GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, - objects.named("9.0.0"), + objects.named(libs.versions.minGradle.get()), ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ee665413a..dd51ca752 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,6 @@ [versions] jdkRelease = "17" +minGradle = "9.0.0" kotlin = "2.3.0-Beta1" moshi = "1.15.2" pluginPublish = "2.0.0" From 04c00183c9111556d1665ff03999aaece9a637ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 12:59:24 +0000 Subject: [PATCH 829/941] Update dependency org.xmlunit:xmlunit-legacy to v2.11.0 (#1825) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dd51ca752..ae6404fe5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ kotlin-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" -xmlunit = "org.xmlunit:xmlunit-legacy:2.10.4" +xmlunit = "org.xmlunit:xmlunit-legacy:2.11.0" moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } From e1b7fb9f53d2e4df4fe5834961b1aecc4b141cd1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 24 Oct 2025 13:08:49 +0000 Subject: [PATCH 830/941] Update Gradle to v9.2.0-rc-3 (#1824) * Update Gradle to v9.2.0-rc-3 * Run on windows-11-arm and Java 21 * Remove DisabledOnOs for Windows ARM64 * Fix `publishShadowJarWithCorrectTargetJvm` * Add `prependText` extension * Revert "Remove DisabledOnOs for Windows ARM64" This reverts commit de17729199843e7f47e689348b7bb8a73dae3776. * Update TODO * Revert "Run on windows-11-arm and Java 21" This reverts commit 5c17df1b03ba1edde8cca1abdc63e6394fc6fb68. --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- gradle/wrapper/gradle-wrapper.properties | 2 +- .../gradle/plugins/shadow/ApplicationPluginTest.kt | 2 +- .../jengelman/gradle/plugins/shadow/JavaPluginsTest.kt | 7 +++---- .../jengelman/gradle/plugins/shadow/PublishingTest.kt | 10 +++++++++- .../jengelman/gradle/plugins/shadow/util/Paths.kt | 7 +++++++ 5 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index dfc438b04..3dab3ad87 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-rc-2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-rc-3-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index e4335374b..3534a320e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -36,7 +36,7 @@ class ApplicationPluginTest : BasePluginTest() { @DisabledOnOs( OS.WINDOWS, architectures = ["aarch64"], - disabledReason = "Cannot use toolchain on Windows ARM64", // TODO: https://github.com/gradle/gradle/issues/29807 + disabledReason = "Cannot use toolchain on Windows ARM64", // TODO: remove when min Gradle is bumped to 9.2+ ) @Test fun integrationWithApplicationPluginAndJavaToolchains() { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index ce55527e3..e307c2541 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -24,12 +24,12 @@ import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import com.github.jengelman.gradle.plugins.shadow.testkit.getMainAttr import com.github.jengelman.gradle.plugins.shadow.testkit.getStream import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.prependText import com.github.jengelman.gradle.plugins.shadow.util.runProcess import kotlin.io.path.appendText import kotlin.io.path.invariantSeparatorsPathString import kotlin.io.path.name import kotlin.io.path.outputStream -import kotlin.io.path.readText import kotlin.io.path.writeText import kotlin.reflect.full.declaredFunctions import kotlin.reflect.jvm.javaMethod @@ -899,13 +899,12 @@ class JavaPluginsTest : BasePluginTest() { @Test fun integrateWithDevelocityBuildScan() { writeClientAndServerModules() - settingsScript.writeText( + settingsScript.prependText( """ plugins { id 'com.gradle.develocity' } - ${settingsScript.readText()} - """.trimIndent(), + """.trimIndent() + lineSeparator, ) val result = runWithSuccess( diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 3bb6bb997..8178d666a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -19,6 +19,7 @@ import com.github.jengelman.gradle.plugins.shadow.testkit.getMainAttr import com.github.jengelman.gradle.plugins.shadow.util.GradleModuleMetadata import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.coordinate +import com.github.jengelman.gradle.plugins.shadow.util.prependText import com.squareup.moshi.JsonAdapter import com.squareup.moshi.Moshi import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory @@ -62,7 +63,7 @@ class PublishingTest : BasePluginTest() { @DisabledOnOs( OS.WINDOWS, architectures = ["aarch64"], - disabledReason = "Cannot use toolchain on Windows ARM64", // TODO: https://github.com/gradle/gradle/issues/29807 + disabledReason = "Cannot use toolchain on Windows ARM64", // TODO: remove when min Gradle is bumped to 9.2+ ) @Test fun publishShadowJarWithCorrectTargetJvm() { @@ -91,6 +92,13 @@ class PublishingTest : BasePluginTest() { val targetJvmAttr11 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "11" val targetJvmAttr8 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "8" + settingsScript.prependText( + """ + plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' + } + """.trimIndent() + lineSeparator, + ) projectScript.appendText( """ java { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt new file mode 100644 index 000000000..79a2d5e5a --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt @@ -0,0 +1,7 @@ +package com.github.jengelman.gradle.plugins.shadow.util + +import java.nio.file.Path +import kotlin.io.path.readText +import kotlin.io.path.writeText + +fun Path.prependText(text: String) = writeText(text + readText()) From e72c6f3cc560edf568a38efb47d39e840503c9a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 17:46:58 +0800 Subject: [PATCH 831/941] Migrate renovate config (#1827) * Migrate config .github/renovate.json5 * Update .github/renovate.json5 --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Zongle Wang --- .github/renovate.json5 | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index ef52f4023..8cf234585 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -3,44 +3,41 @@ extends: [ 'config:recommended', ], - "labels": [ - "dependencies", + labels: [ + 'dependencies', ], - "packageRules": [ + packageRules: [ { // https://github.com/tcurdt/jdependency/issues/325 - "groupName": "ASM and jdependency", - "matchPackageNames": [ - "org.vafer:jdependency", - "org.ow2.asm:asm-commons" + groupName: 'ASM and jdependency', + matchPackageNames: [ + 'org.vafer:jdependency', + 'org.ow2.asm:asm-commons', ], }, { - "groupName": "Develocity", - "matchPackageNames": [ - "com.gradle:develocity-gradle-plugin", - "com.gradle.develocity:com.gradle.develocity.gradle.plugin", + groupName: 'Develocity', + matchPackageNames: [ + 'com.gradle:develocity-gradle-plugin', + 'com.gradle.develocity:com.gradle.develocity.gradle.plugin', ], }, ], ignorePresets: [ - // Ensure we get the latest version and are not pinned to old versions. 'workarounds:javaLTSVersions', ], customManagers: [ - // Update .java-version file with the latest JDK version. { customType: 'regex', - fileMatch: [ - '\\.java-version$', + managerFilePatterns: [ + '/\\.java-version$/', ], matchStrings: [ '(?.*)\\n', ], datasourceTemplate: 'java-version', depNameTemplate: 'java', - // Only write the major version. extractVersionTemplate: '^(?\\d+)', }, - ] + ], } From a3121a0aa3b83331ceb8a0f3696f98d6b8c31a65 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 17:49:45 +0800 Subject: [PATCH 832/941] Update kotlin monorepo to v2.3.0-Beta2 (#1826) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ae6404fe5..ae0561e51 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] jdkRelease = "17" minGradle = "9.0.0" -kotlin = "2.3.0-Beta1" +kotlin = "2.3.0-Beta2" moshi = "1.15.2" pluginPublish = "2.0.0" From f1f19481c50904aecaaf74389c8c3ac8f36b3247 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 28 Oct 2025 10:03:06 +0000 Subject: [PATCH 833/941] Update dependency java to v25 (#1821) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/.java-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.java-version b/.github/workflows/.java-version index a45fd52cc..7273c0fa8 100644 --- a/.github/workflows/.java-version +++ b/.github/workflows/.java-version @@ -1 +1 @@ -24 +25 From 4ec64235352ed8d0af8ca817fe56d5c19e30ebed Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 28 Oct 2025 18:12:36 +0800 Subject: [PATCH 834/941] Add comments back --- .github/renovate.json5 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 8cf234585..e63b89d9d 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -24,9 +24,11 @@ }, ], ignorePresets: [ + // Ensure we get the latest version and are not pinned to old versions. 'workarounds:javaLTSVersions', ], customManagers: [ + // Update .java-version file with the latest JDK version. { customType: 'regex', managerFilePatterns: [ @@ -37,6 +39,7 @@ ], datasourceTemplate: 'java-version', depNameTemplate: 'java', + // Only write the major version. extractVersionTemplate: '^(?\\d+)', }, ], From c24ae19c6d12537f7cfb8e679a31aa349a5a16ca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 01:36:21 +0000 Subject: [PATCH 835/941] Update Gradle to v9.2.0 (#1828) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 3dab3ad87..bad7c2462 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-rc-3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From f5fbd4c4832873e5e605229f84d99ee16f66352b Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 30 Oct 2025 11:20:49 +0800 Subject: [PATCH 836/941] Update compatibility sheet Removed outdated Shadow plugin version information. --- docs/README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/docs/README.md b/docs/README.md index 39ff69f74..78d388b57 100644 --- a/docs/README.md +++ b/docs/README.md @@ -22,9 +22,6 @@ dependent libraries into the output jar without incurring the I/O overhead of ex | Shadow Version | Min Gradle Version | Min Java Version | Plugin ID | |----------------|--------------------|------------------|------------------------------------------------------| -| 5.2.0 - 6.1.0 | 5.x - 6.x | 7 | [`com.github.johnrengelman.shadow`][johnrengelman's] | -| 6.1.0+ | 6.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | -| 7.0.0+ | 7.x | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.0.0+ | 8.0 | 8 | [`com.github.johnrengelman.shadow`][johnrengelman's] | | 8.3.0+ | 8.3 | 8 | [`com.gradleup.shadow`][gradleup's] | | 9.0.0+ | 8.11 | 11 | [`com.gradleup.shadow`][gradleup's] | From 6a498a87465c0e7961c2ca6e738bf7a5c98b38fb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 30 Oct 2025 14:28:12 +0800 Subject: [PATCH 837/941] Update docs and tests for groovy and scala plugins (#1831) * Remove tests for groovy and scala plugins * Update `docs/groovy-and-scala-plugins/README.md` * Update src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/BasePluginTest.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update desc --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/groovy-and-scala-plugins/README.md | 22 +--------- .../gradle/plugins/shadow/BasePluginTest.kt | 20 +-------- .../gradle/plugins/shadow/GroovyPluginTest.kt | 41 ------------------ .../gradle/plugins/shadow/ScalaPluginTest.kt | 42 ------------------- .../gradle/plugins/shadow/util/JvmLang.kt | 2 - 5 files changed, 3 insertions(+), 124 deletions(-) delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt delete mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt diff --git a/docs/groovy-and-scala-plugins/README.md b/docs/groovy-and-scala-plugins/README.md index e9979d50d..b0e670610 100644 --- a/docs/groovy-and-scala-plugins/README.md +++ b/docs/groovy-and-scala-plugins/README.md @@ -12,11 +12,6 @@ For Groovy: id("com.gradleup.shadow") } - dependencies { - // If you don't want the Groovy standard library to be shadowed, please replace `implementation` with `api`. - implementation(localGroovy()) - } - tasks.shadowJar { manifest { // Optionally, set the main class for the shadowed JAR. @@ -33,11 +28,6 @@ For Groovy: id 'com.gradleup.shadow' } - dependencies { - // If you don't want the Groovy standard library to be shadowed, please replace `implementation` with `api`. - implementation localGroovy() - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { manifest { // Optionally, set the main class for the shadowed JAR. @@ -56,11 +46,6 @@ For Scala: id("com.gradleup.shadow") } - dependencies { - // If you don't want the Scala standard library to be shadowed, please replace `implementation` with `api`. - implementation("org.scala-lang:scala-library:2.13.16") - } - tasks.shadowJar { manifest { // Optionally, set the main class for the shadowed JAR. @@ -77,11 +62,6 @@ For Scala: id 'com.gradleup.shadow' } - dependencies { - // If you don't want the Scala standard library to be shadowed, please replace `implementation` with `api`. - implementation 'org.scala-lang:scala-library:2.13.16' - } - tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { manifest { // Optionally, set the main class for the shadowed JAR. @@ -89,3 +69,5 @@ For Scala: } } ``` + +You can customize the other configurations of the `shadowJar` task as needed, just like with Java projects. 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 4644a3d91..8e8fdebfe 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 @@ -226,9 +226,7 @@ abstract class BasePluginTest { jvmLang: JvmLang = JvmLang.Java, content: () -> String = { when (jvmLang) { - JvmLang.Groovy, - JvmLang.Java, - -> { + JvmLang.Java -> { val imports = if (withImports) "import junit.framework.Test;" else "" val classRef = if (withImports) "\"Refs: \" + Test.class.getName()" else "\"Refs: null\"" """ @@ -259,22 +257,6 @@ abstract class BasePluginTest { } """.trimIndent() } - JvmLang.Scala -> { - val imports = if (withImports) "import junit.framework.Test" else "" - val classRef = if (withImports) "\"Refs: \" + classOf[Test].getName" else "\"Refs: null\"" - """ - package $packageName - $imports - object $className { - def main(args: Array[String]): Unit = { - if (args.isEmpty) throw new IllegalArgumentException("No arguments provided.") - val content = s"Hello, World! (%s) from $className".format(args: _*) - println(content) - println($classRef) - } - } - """.trimIndent() - } } }, ): String { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt deleted file mode 100644 index 28bfa7e77..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/GroovyPluginTest.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import assertk.assertThat -import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly -import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import kotlin.io.path.appendText -import kotlin.io.path.writeText -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -class GroovyPluginTest : BasePluginTest() { - @BeforeEach - override fun beforeEach() { - super.beforeEach() - projectScript.writeText(getDefaultProjectBuildScript(plugin = "groovy")) - } - - @Test - fun compatGroovy() { - val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Groovy) - projectScript.appendText( - """ - dependencies { - compileOnly localGroovy() - implementation 'junit:junit:3.8.2' - } - """.trimIndent(), - ) - - runWithSuccess(shadowJarPath) - - assertThat(outputShadowedJar).useAll { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) - } - } -} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt deleted file mode 100644 index 6046d6a0b..000000000 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ScalaPluginTest.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.github.jengelman.gradle.plugins.shadow - -import assertk.assertThat -import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly -import com.github.jengelman.gradle.plugins.shadow.util.JvmLang -import kotlin.io.path.appendText -import kotlin.io.path.writeText -import org.junit.jupiter.api.BeforeEach -import org.junit.jupiter.api.Test - -class ScalaPluginTest : BasePluginTest() { - @BeforeEach - override fun beforeEach() { - super.beforeEach() - projectScript.writeText(getDefaultProjectBuildScript(plugin = "scala")) - } - - @Test - fun compatScala() { - val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Scala) - projectScript.appendText( - """ - dependencies { - compileOnly 'org.scala-lang:scala-library:2.13.16' - implementation 'junit:junit:3.8.2' - } - """.trimIndent(), - ) - - runWithSuccess(shadowJarPath) - - assertThat(outputShadowedJar).useAll { - containsOnly( - "my/", - "my/Main$.class", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) - } - } -} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt index 3cc550d7d..2243ef674 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt @@ -3,10 +3,8 @@ package com.github.jengelman.gradle.plugins.shadow.util enum class JvmLang( val suffix: String, ) { - Groovy("groovy"), Java("java"), Kotlin("kt"), - Scala("scala"), ; override fun toString(): String = name.lowercase() From ca5edaff4b6b304e9bf34d784ff5a33cb0874f93 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Nov 2025 07:26:56 +0800 Subject: [PATCH 838/941] Update dependency org.junit:junit-bom to v6.0.1 (#1832) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ae0561e51..3afe9f1d9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -31,7 +31,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktlint = "com.pinterest.ktlint:ktlint-cli:1.7.1" -junit-bom = "org.junit:junit-bom:6.0.0" +junit-bom = "org.junit:junit-bom:6.0.1" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From 5f6a894a2863f6d319db7afdf8116a534043c801 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 1 Nov 2025 12:44:55 +0800 Subject: [PATCH 839/941] Set ignored archiveClassifier for standard Jar (#1833) --- docs/publishing/README.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/publishing/README.md b/docs/publishing/README.md index 4c1e27828..06112ad68 100644 --- a/docs/publishing/README.md +++ b/docs/publishing/README.md @@ -323,6 +323,24 @@ If you don't need the standard JAR, you can disable the `jar` task like: } ``` +Or set a different `archiveClassifier` for the standard [`Jar`][Jar] like: + +=== "Kotlin" + + ```kotlin + tasks.jar { + archiveClassifier = "ignored" + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('jar', Jar) { + archiveClassifier = 'ignored' + } + ``` + ## Publishing the Shadowed Gradle Plugins The Gradle Publish Plugin introduced support for plugins packaged with Shadow in version 1.0.0. From 8a9802cd76a1c6cd47d68531b27dd2678981793e Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 2 Nov 2025 22:58:44 +0800 Subject: [PATCH 840/941] New termsOfUseUrl (#1834) https://docs.gradle.com/develocity/gradle-plugin/current/#connecting_to_scans_gradle_com --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index d172b492c..8ba118ac5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,7 +18,7 @@ plugins { develocity { buildScan { - termsOfUseUrl = "https://gradle.com/terms-of-service" + termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use" termsOfUseAgree = "yes" // TODO: https://github.com/gradle/gradle/issues/22879 val isCI = providers.environmentVariable("CI").isPresent From 2355ea0e4ec0864e3875cf556fd3e7e22079347b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 8 Nov 2025 05:54:36 +0000 Subject: [PATCH 841/941] Update dependency commons-io:commons-io to v2.21.0 (#1837) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3afe9f1d9..dca606aee 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ pluginPublish = "2.0.0" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" -apache-commonsIo = "commons-io:commons-io:2.20.0" +apache-commonsIo = "commons-io:commons-io:2.21.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.2" apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.11" asm = "org.ow2.asm:asm-commons:9.9" From 98679985ae5c3e470c38765800ce247b94274586 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 10 Nov 2025 05:09:58 +0800 Subject: [PATCH 842/941] Disable more rules for Lint --- build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.gradle.kts b/build.gradle.kts index 160a7203d..afb1d848f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -51,6 +51,8 @@ lint { ignoreTestSources = true warningsAsErrors = true disable += "NewerVersionAvailable" + disable += "GradleDependency" + disable += "AndroidGradlePluginVersion" } spotless { From 406eedfc215a94f340e4dcb59ddf155d0b8d8a39 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 07:19:40 +0800 Subject: [PATCH 843/941] Update plugin android-lint to v8.13.1 (#1838) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dca606aee..527ca08ff 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -36,7 +36,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.13.0" +android-lint = "com.android.lint:8.13.1" jetbrains-dokka = "org.jetbrains.dokka:2.1.0" mavenPublish = "com.vanniktech.maven.publish:0.34.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From 15e8f0f95e0aed927daf9d13347b1a6057f597af Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Nov 2025 00:42:03 +0800 Subject: [PATCH 844/941] Simplify min Kotlin version check (#1839) --- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 4 ++-- .../plugins/shadow/internal/KotlinCompat.kt | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 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 6f2e16030..3467cd562 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.internal.isAtLeastKgpVersion +import com.github.jengelman.gradle.plugins.shadow.internal.isAtLeastKgp import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.SHADOW_JAR_TASK_NAME import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar.Companion.registerShadowJarCommon import org.gradle.api.Plugin @@ -36,7 +36,7 @@ public abstract class ShadowKmpPlugin : Plugin { }, ) - if (!isAtLeastKgpVersion(1, 9, 0)) return@registerShadowJarCommon + if (!isAtLeastKgp("1.9.0")) return@registerShadowJarCommon @OptIn(ExperimentalKotlinGradlePluginApi::class) target.mainRun { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt index b55a70516..7797b6d97 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt @@ -5,16 +5,16 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin internal const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform" -internal fun Project.isAtLeastKgpVersion( - major: Int, - minor: Int, - patch: Int, +internal fun Project.isAtLeastKgp( + version: String, id: String = KOTLIN_MULTIPLATFORM_PLUGIN_ID, ): Boolean { - val plugin = plugins.getPlugin(id) as KotlinBasePlugin - val elements = plugin.pluginVersion.takeWhile { it != '-' }.split(".") - val kgpMajor = elements[0].toInt() - val kgpMinor = elements[1].toInt() - val kgpPatch = elements[2].toInt() - return kgpMajor > major || (kgpMajor == major && (kgpMinor > minor || (kgpMinor == minor && kgpPatch >= patch))) + val (major, minor, patch) = version.normalizeVersion() + val (actualMajor, actualMinor, actualPatch) = (plugins.getPlugin(id) as KotlinBasePlugin).pluginVersion.normalizeVersion() + return KotlinVersion(actualMajor, actualMinor, actualPatch) >= KotlinVersion(major, minor, patch) +} + +private fun String.normalizeVersion(): Triple { + val (major, minor, patch) = takeWhile { it != '-' }.split(".").map { it.toInt() } + return Triple(major, minor, patch) } From 07cbe3d12aaf23cadab5662e7e6ed4901e4113ee Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 07:43:58 +0800 Subject: [PATCH 845/941] Update plugin mavenPublish to v0.35.0 (#1840) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 527ca08ff..e7e9787ee 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,6 +38,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:8.13.1" jetbrains-dokka = "org.jetbrains.dokka:2.1.0" -mavenPublish = "com.vanniktech.maven.publish:0.34.0" +mavenPublish = "com.vanniktech.maven.publish:0.35.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } spotless = "com.diffplug.spotless:8.0.0" From cba083d489446eb23fda25a7bfa59e3af93e32b1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 12 Nov 2025 15:12:02 +0800 Subject: [PATCH 846/941] Prefer KotlinToolingVersion than KotlinVersion (#1841) --- .../gradle/plugins/shadow/internal/KotlinCompat.kt | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt index 7797b6d97..6e974d204 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/KotlinCompat.kt @@ -2,6 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.internal import org.gradle.api.Project import org.jetbrains.kotlin.gradle.plugin.KotlinBasePlugin +import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion internal const val KOTLIN_MULTIPLATFORM_PLUGIN_ID = "org.jetbrains.kotlin.multiplatform" @@ -9,12 +10,6 @@ internal fun Project.isAtLeastKgp( version: String, id: String = KOTLIN_MULTIPLATFORM_PLUGIN_ID, ): Boolean { - val (major, minor, patch) = version.normalizeVersion() - val (actualMajor, actualMinor, actualPatch) = (plugins.getPlugin(id) as KotlinBasePlugin).pluginVersion.normalizeVersion() - return KotlinVersion(actualMajor, actualMinor, actualPatch) >= KotlinVersion(major, minor, patch) -} - -private fun String.normalizeVersion(): Triple { - val (major, minor, patch) = takeWhile { it != '-' }.split(".").map { it.toInt() } - return Triple(major, minor, patch) + val actual = (plugins.getPlugin(id) as KotlinBasePlugin).pluginVersion + return KotlinToolingVersion(actual) >= KotlinToolingVersion(version) } From 53249458b43caa2898a69bfceadbb5009c71cfc2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 15 Nov 2025 09:58:47 +0800 Subject: [PATCH 847/941] Update dependency com.pinterest.ktlint:ktlint-cli to v1.8.0 (#1846) * Update dependency com.pinterest.ktlint:ktlint-cli to v1.8.0 * Disable `mixed-condition-operators` --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- .editorconfig | 1 + gradle/libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.editorconfig b/.editorconfig index ca8471ed5..ccbd273ec 100755 --- a/.editorconfig +++ b/.editorconfig @@ -17,6 +17,7 @@ ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 ij_kotlin_packages_to_use_import_on_demand = unset ktlint_code_style = intellij_idea ktlint_standard_function-expression-body = disabled +ktlint_standard_mixed-condition-operators = disabled [*.md] trim_trailing_whitespace = false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e7e9787ee..c0925c442 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -29,7 +29,7 @@ pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.r androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. -ktlint = "com.pinterest.ktlint:ktlint-cli:1.7.1" +ktlint = "com.pinterest.ktlint:ktlint-cli:1.8.0" junit-bom = "org.junit:junit-bom:6.0.1" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" From 846046878dd9aa3086f5e1a7b5c54c487f19d31e Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 15 Nov 2025 10:20:02 +0800 Subject: [PATCH 848/941] Remove TODO about Gradle issue 22879 --- settings.gradle.kts | 1 - 1 file changed, 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 8ba118ac5..78d76bbfd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,7 +20,6 @@ develocity { buildScan { termsOfUseUrl = "https://gradle.com/help/legal-terms-of-use" termsOfUseAgree = "yes" - // TODO: https://github.com/gradle/gradle/issues/22879 val isCI = providers.environmentVariable("CI").isPresent publishing.onlyIf { isCI } } From 7027e20794fd4568906c92b1f9156d614d79a8af Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 15 Nov 2025 10:28:29 +0800 Subject: [PATCH 849/941] Prefer providers.gradleProperty over project.findProperty (#1836) --- docs/changes/README.md | 4 ++++ .../jengelman/gradle/plugins/shadow/internal/GradleCompat.kt | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index e6ac914f2..a178c5b27 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -9,6 +9,10 @@ - Update ASM and jdependency to support Java 26. ([#1799](https://github.com/GradleUp/shadow/pull/1799)) - Bump min Gradle requirement to 9.0.0. ([#1801](https://github.com/GradleUp/shadow/pull/1801)) +### Fixed + +- Fix Develocity integration when Isolated Projects enabled. ([#1836](https://github.com/GradleUp/shadow/pull/1836)) + ## [9.2.2](https://github.com/GradleUp/shadow/compare/9.2.2) - 2025-09-26 ### Fixed diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index e6a744693..f06880ab4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -45,8 +45,9 @@ internal inline val Project.javaPluginExtension: JavaPluginExtension internal inline val Project.javaToolchainService: JavaToolchainService get() = extensions.getByType(JavaToolchainService::class.java) -@Suppress("GradleProjectIsolation") // TODO: we can't call 'providers.gradleProperty' instead due to https://github.com/gradle/gradle/issues/23572. -internal fun Project.findOptionalProperty(propertyName: String): String? = findProperty(propertyName)?.toString() +@Suppress("GradleProjectIsolation") // TODO: https://github.com/gradle/gradle/issues/23572 +internal fun Project.findOptionalProperty(propertyName: String): String? = providers.gradleProperty(propertyName).orNull + ?: findProperty(propertyName)?.toString() internal fun Project.addBuildScanCustomValues() { val develocity = extensions.findByType(DevelocityConfiguration::class.java) ?: return From a4639c7e318175a08f35fa67b6371163b88a3719 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 15 Nov 2025 10:35:13 +0800 Subject: [PATCH 850/941] Temporarily disable linkspector action --- .github/workflows/links.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index 1e90d5bca..e094cb688 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -34,6 +34,7 @@ jobs: pip install mkdocs-material mkdocs build --strict - uses: umbrelladocs/action-linkspector@v1 + if: false # TODO: https://github.com/UmbrellaDocs/linkspector/issues/149 with: reporter: github-pr-check fail_on_error: true From 3ce3b73aab7e309ed8570dadd6429dd874f6271d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 16 Nov 2025 09:54:16 +0800 Subject: [PATCH 851/941] Add PatternFilterableResourceTransformer and expose patternSet (#1849) * New `PatternFilterableResourceTransformer` * Update changelog * Public `patternSet` * Fix ctor of `ServiceFileTransformer` * Override `canTransformResource` * Add Kdoc --- api/shadow.api | 38 ++++++++++++------- docs/changes/README.md | 5 +++ .../PatternFilterableResourceTransformer.kt | 28 ++++++++++++++ .../transformers/ServiceFileTransformer.kt | 18 +-------- 4 files changed, 59 insertions(+), 30 deletions(-) create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt diff --git a/api/shadow.api b/api/shadow.api index 5e2efff0c..81644f95e 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -385,6 +385,29 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestRes public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } +public abstract class com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer, org/gradle/api/tasks/util/PatternFilterable { + public fun (Lorg/gradle/api/tasks/util/PatternSet;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun exclude (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun getExcludes ()Ljava/util/Set; + public fun getIncludes ()Ljava/util/Set; + public fun getName ()Ljava/lang/String; + public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public final fun getPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; + public fun hasTransformedResource ()Z + public fun include (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun setExcludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun setIncludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + public class com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z @@ -447,26 +470,13 @@ public final class com/github/jengelman/gradle/plugins/shadow/transformers/Resou public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer, org/gradle/api/tasks/util/PatternFilterable { +public class com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer { public fun ()V public fun (Lorg/gradle/api/tasks/util/PatternSet;)V public synthetic fun (Lorg/gradle/api/tasks/util/PatternSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V - public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public fun exclude (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; - public fun exclude (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; - public fun exclude (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; - public fun exclude ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; - public fun getExcludes ()Ljava/util/Set; - public fun getIncludes ()Ljava/util/Set; public fun getPath ()Ljava/lang/String; public fun hasTransformedResource ()Z - public fun include (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; - public fun include (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; - public fun include (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; - public fun include ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public fun setExcludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; - public fun setIncludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; public fun setPath (Ljava/lang/String;)V public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } diff --git a/docs/changes/README.md b/docs/changes/README.md index a178c5b27..099b558b3 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,11 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.2.2...HEAD) - 2025-xx-xx +### Added + +- Add `PatternFilterableResourceTransformer` to simplify pattern based `ResourceTransformer`s. ([#1849](https://github.com/GradleUp/shadow/pull/1849)) +- Expose `patternSet` of `ServiceFileTransformer` as `public`. ([#1849](https://github.com/GradleUp/shadow/pull/1849)) + ### Changed - Change the group of `startShadowScripts` from `application` to `other`. ([#1797](https://github.com/GradleUp/shadow/pull/1797)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt new file mode 100644 index 000000000..466b69996 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt @@ -0,0 +1,28 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.util.PatternFilterable +import org.gradle.api.tasks.util.PatternSet + +/** + * A base class for resource transformers that support pattern filtering. + * + * @param patternSet The [PatternSet] used for filtering resources. + */ +public abstract class PatternFilterableResourceTransformer( + @Internal public val patternSet: PatternSet, +) : ResourceTransformer by ResourceTransformer.Companion, + PatternFilterable by patternSet { + + override fun canTransformResource(element: FileTreeElement): Boolean { + return patternSet.asSpec.isSatisfiedBy(element) + } + + @Input // Trigger task executions after includes changed. + override fun getIncludes(): MutableSet = patternSet.includes + + @Input // Trigger task executions after excludes changed. + override fun getExcludes(): MutableSet = patternSet.excludes +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 87e888583..3b56e2a7d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -4,10 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import com.github.jengelman.gradle.plugins.shadow.transformers.GroovyExtensionModuleTransformer.Companion.PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement -import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.util.PatternFilterable import org.gradle.api.tasks.util.PatternSet /** @@ -25,11 +22,10 @@ import org.gradle.api.tasks.util.PatternSet */ @CacheableTransformer public open class ServiceFileTransformer( - private val patternSet: PatternSet = PatternSet() + patternSet: PatternSet = PatternSet() .include(SERVICES_PATTERN) .exclude(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), -) : ResourceTransformer, - PatternFilterable by patternSet { +) : PatternFilterableResourceTransformer(patternSet = patternSet) { @get:Internal internal val serviceEntries = mutableMapOf>() @@ -40,10 +36,6 @@ public open class ServiceFileTransformer( patternSet.setIncludes(listOf("$value/**")) } - override fun canTransformResource(element: FileTreeElement): Boolean { - return patternSet.asSpec.isSatisfiedBy(element) - } - override fun transform(context: TransformerContext) { val resource = path + "/" + context.relocators.relocateClass(context.path.substringAfter("$path/")) @@ -63,12 +55,6 @@ public open class ServiceFileTransformer( } } - @Input // Trigger task executions after includes changed. - override fun getIncludes(): MutableSet = patternSet.includes - - @Input // Trigger task executions after excludes changed. - override fun getExcludes(): MutableSet = patternSet.excludes - private companion object { private const val SERVICES_PATH = "META-INF/services" private const val SERVICES_PATTERN = "$SERVICES_PATH/**" From 6a1ca8b256b5be3192d385ff87e322c1d9974a83 Mon Sep 17 00:00:00 2001 From: Goooler Date: Mon, 17 Nov 2025 09:10:16 +0800 Subject: [PATCH 852/941] Run action-linkspector on ubuntu-latest --- .github/workflows/links.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index e094cb688..9abad4007 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -19,7 +19,7 @@ on: jobs: check-links: - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-latest # TODO: https://github.com/UmbrellaDocs/linkspector/issues/149 if: github.event.repository.fork == false steps: - uses: actions/checkout@v5 @@ -34,7 +34,6 @@ jobs: pip install mkdocs-material mkdocs build --strict - uses: umbrelladocs/action-linkspector@v1 - if: false # TODO: https://github.com/UmbrellaDocs/linkspector/issues/149 with: reporter: github-pr-check fail_on_error: true From 29a06a5ae245884592bb8a406b5d4caa43fc9a60 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Nov 2025 17:05:20 +0800 Subject: [PATCH 853/941] Support patternSet for license and notice transformers (#1850) * Expose `patternSet` of `ApacheLicenseResourceTransformer` as `public` * Mark `JvmOverloads` for ctors * Expose `patternSet` of `ApacheNoticeResourceTransformer` as `public` * Restore props back * Test `canTransformByPattern` * Document `Configuring Resource Transformer Filtering by Pattern` * Update docs/configuration/merging/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update docs/changes/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix doc --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 14 +++---- docs/changes/README.md | 2 + docs/configuration/merging/README.md | 39 +++++++++++++++++++ .../ApacheLicenseResourceTransformer.kt | 19 ++++----- .../ApacheNoticeResourceTransformer.kt | 24 +++++++----- .../transformers/ServiceFileTransformer.kt | 2 +- .../ApacheLicenseResourceTransformerTest.kt | 10 +++++ .../ApacheNoticeResourceTransformerTest.kt | 10 +++++ 8 files changed, 92 insertions(+), 28 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index 81644f95e..6712ce3fc 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -250,19 +250,15 @@ public final class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar$Co public final synthetic fun getShadowJar (Lorg/gradle/api/tasks/TaskContainer;)Lorg/gradle/api/tasks/TaskProvider; } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer { public fun ()V - public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z - public fun getName ()Ljava/lang/String; - public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; - public fun hasTransformedResource ()Z - public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V + public fun (Lorg/gradle/api/tasks/util/PatternSet;)V + public synthetic fun (Lorg/gradle/api/tasks/util/PatternSet;ILkotlin/jvm/internal/DefaultConstructorMarker;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V - public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/tasks/util/PatternSet;)V public fun getAddHeader ()Lorg/gradle/api/provider/Property; public fun getCharsetName ()Lorg/gradle/api/provider/Property; public fun getCopyright ()Lorg/gradle/api/provider/Property; diff --git a/docs/changes/README.md b/docs/changes/README.md index 099b558b3..e7fc721fe 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,6 +7,8 @@ - Add `PatternFilterableResourceTransformer` to simplify pattern based `ResourceTransformer`s. ([#1849](https://github.com/GradleUp/shadow/pull/1849)) - Expose `patternSet` of `ServiceFileTransformer` as `public`. ([#1849](https://github.com/GradleUp/shadow/pull/1849)) +- Expose `patternSet` of `ApacheLicenseResourceTransformer` as `public`. ([#1850](https://github.com/GradleUp/shadow/pull/1850)) +- Expose `patternSet` of `ApacheNoticeResourceTransformer` as `public`. ([#1850](https://github.com/GradleUp/shadow/pull/1850)) ### Changed diff --git a/docs/configuration/merging/README.md b/docs/configuration/merging/README.md index 0eea6f3f8..43ca7f27c 100644 --- a/docs/configuration/merging/README.md +++ b/docs/configuration/merging/README.md @@ -546,6 +546,41 @@ It must be added using the [`transform`][ShadowJar.transform] methods. } ``` +## Configuring Resource Transformer Filtering by Pattern + +There are lots of built-in [`ResourceTransformer`][ResourceTransformer]s provided by Shadow. Some of them extend +[`PatternFilterableResourceTransformer`][PatternFilterableResourceTransformer], which extends +[`PatternFilterable`][PatternFilterable] to provide `include`/`exclude` pattern filtering capabilities. e.g. + +- [`ApacheLicenseResourceTransformer`][ApacheLicenseResourceTransformer] +- [`ApacheNoticeResourceTransformer`][ApacheNoticeResourceTransformer] +- [`ServiceFileTransformer`][ServiceFileTransformer] +- ... + +You can use `include`/`exclude` and more methods to configure the patterns for those +[`ResourceTransformer`][ResourceTransformer]s that support it. For example: + +=== "Kotlin" + + ```kotlin + tasks.shadowJar { + transform() { + include("META-INF/LICENSE.*") + exclude("META-INF/LICENSE.log") + } + } + ``` + +=== "Groovy" + + ```groovy + tasks.named('shadowJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { + transform(com.github.jengelman.gradle.plugins.shadow.transformers.ApacheLicenseResourceTransformer) { + include 'META-INF/LICENSE.*' + exclude 'META-INF/LICENSE.log' + } + } + ``` [AbstractCopyTask]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.AbstractCopyTask.html @@ -558,8 +593,12 @@ It must be added using the [`transform`][ShadowJar.transform] methods. [Jar]: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Jar.html [Log4j2PluginsCacheFileTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-log4j2-plugins-cache-file-transformer/index.html [ResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-resource-transformer/index.html +[ApacheLicenseResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-apache-license-resource-transformer/index.html +[ApacheNoticeResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-apache-notice-resource-transformer/index.html [ServiceFileTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-service-file-transformer/index.html [PreserveFirstFoundResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-preserve-first-found-resource-transformer/index.html +[PatternFilterable]: https://docs.gradle.org/current/kotlin-dsl/gradle/org.gradle.api.tasks.util/-pattern-filterable/index.html +[PatternFilterableResourceTransformer]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.transformers/-pattern-filterable-resource-transformer/index.html [ShadowJar.append]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/append.html [ShadowJar.failOnDuplicateEntries]: ../../api/shadow/com.github.jengelman.gradle.plugins.shadow.tasks/-shadow-jar/fail-on-duplicate-entries.html [ShadowJar.from]: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object,%20org.gradle.api.Action) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index 71f29c862..f28ec92b3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -1,6 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import org.gradle.api.file.FileTreeElement +import org.gradle.api.tasks.util.PatternSet /** * Prevents duplicate copies of the license. @@ -10,14 +10,15 @@ import org.gradle.api.file.FileTreeElement * @author John Engelman */ @CacheableTransformer -public open class ApacheLicenseResourceTransformer : ResourceTransformer by ResourceTransformer.Companion { - override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.path - return LICENSE_PATH.equals(path, ignoreCase = true) || - LICENSE_TXT_PATH.regionMatches(0, path, 0, LICENSE_TXT_PATH.length, ignoreCase = true) || - LICENSE_MD_PATH.regionMatches(0, path, 0, LICENSE_MD_PATH.length, ignoreCase = true) - } - +public open class ApacheLicenseResourceTransformer @JvmOverloads constructor( + patternSet: PatternSet = PatternSet() + .apply { isCaseSensitive = false } + .include( + LICENSE_PATH, + LICENSE_TXT_PATH, + LICENSE_MD_PATH, + ), +) : PatternFilterableResourceTransformer(patternSet = patternSet) { private companion object { private const val LICENSE_PATH = "META-INF/LICENSE" private const val LICENSE_TXT_PATH = "META-INF/LICENSE.txt" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 40109113a..b142ff271 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -9,10 +9,10 @@ import java.util.Locale import java.util.TreeSet import javax.inject.Inject import org.apache.tools.zip.ZipOutputStream -import org.gradle.api.file.FileTreeElement import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Property import org.gradle.api.tasks.Input +import org.gradle.api.tasks.util.PatternSet /** * Merges `META-INF/NOTICE.TXT` files. @@ -22,9 +22,10 @@ import org.gradle.api.tasks.Input * @author John Engelman */ @CacheableTransformer -public open class ApacheNoticeResourceTransformer @Inject constructor( +public open class ApacheNoticeResourceTransformer( final override val objectFactory: ObjectFactory, -) : ResourceTransformer { + patternSet: PatternSet, +) : PatternFilterableResourceTransformer(patternSet) { private val entries = mutableSetOf() private val organizationEntries = mutableMapOf>() private inline val charset get() = Charset.forName(charsetName.get()) @@ -75,12 +76,17 @@ public open class ApacheNoticeResourceTransformer @Inject constructor( @get:Input public open val charsetName: Property = objectFactory.property(Charsets.UTF_8.name()) - override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.path - return NOTICE_PATH.equals(path, ignoreCase = true) || - NOTICE_TXT_PATH.equals(path, ignoreCase = true) || - NOTICE_MD_PATH.equals(path, ignoreCase = true) - } + @Inject + public constructor(objectFactory: ObjectFactory) : this( + objectFactory, + patternSet = PatternSet() + .apply { isCaseSensitive = false } + .include( + NOTICE_PATH, + NOTICE_TXT_PATH, + NOTICE_MD_PATH, + ), + ) override fun transform(context: TransformerContext) { val projectName = projectName.get() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 3b56e2a7d..88bf1ce5f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -21,7 +21,7 @@ import org.gradle.api.tasks.util.PatternSet * @author John Engelman */ @CacheableTransformer -public open class ServiceFileTransformer( +public open class ServiceFileTransformer @JvmOverloads constructor( patternSet: PatternSet = PatternSet() .include(SERVICES_PATTERN) .exclude(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt index 556e9c6bc..fc27f7fc8 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt @@ -23,4 +23,14 @@ class ApacheLicenseResourceTransformerTest : BaseTransformerTest Date: Mon, 17 Nov 2025 17:34:35 +0800 Subject: [PATCH 854/941] Support overriding output path of `ApacheNoticeResourceTransformer` (#1851) * Add `outputPath` for `ApacheNoticeResourceTransformer` * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt Co-authored-by: Robert Stupp * Update changelog * Dump API * Test `overrideOutputPathOfNoticeFile` --------- Co-authored-by: Robert Stupp --- api/shadow.api | 1 + docs/changes/README.md | 1 + .../shadow/transformers/TransformersTest.kt | 39 +++++++++++++++++++ .../ApacheNoticeResourceTransformer.kt | 10 ++++- 4 files changed, 50 insertions(+), 1 deletion(-) diff --git a/api/shadow.api b/api/shadow.api index 6712ce3fc..ed46f45a4 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -266,6 +266,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNotic public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun getOrganizationName ()Lorg/gradle/api/provider/Property; public fun getOrganizationURL ()Lorg/gradle/api/provider/Property; + public fun getOutputPath ()Lorg/gradle/api/provider/Property; public fun getPreamble1 ()Lorg/gradle/api/provider/Property; public fun getPreamble2 ()Lorg/gradle/api/provider/Property; public fun getPreamble3 ()Lorg/gradle/api/provider/Property; diff --git a/docs/changes/README.md b/docs/changes/README.md index e7fc721fe..487920713 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -9,6 +9,7 @@ - Expose `patternSet` of `ServiceFileTransformer` as `public`. ([#1849](https://github.com/GradleUp/shadow/pull/1849)) - Expose `patternSet` of `ApacheLicenseResourceTransformer` as `public`. ([#1850](https://github.com/GradleUp/shadow/pull/1850)) - Expose `patternSet` of `ApacheNoticeResourceTransformer` as `public`. ([#1850](https://github.com/GradleUp/shadow/pull/1850)) +- Support overriding output path of `ApacheNoticeResourceTransformer`. ([#1851](https://github.com/GradleUp/shadow/pull/1851)) ### Changed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 69bb88224..cba2f7ebb 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -251,6 +251,45 @@ class TransformersTest : BaseTransformerTest() { } } + @Test + fun overrideOutputPathOfNoticeFile() { + val noticeEntry = "META-INF/NOTICE" + val customNoticeEntry = "META-INF/CUSTOM_NOTICE" + val one = buildJarOne { + insert(noticeEntry, "Notice from A") + } + val two = buildJarTwo { + insert(noticeEntry, "Notice from B") + } + projectScript.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = "addHeader = false; outputPath = '$customNoticeEntry'", + ), + ) + + runWithSuccess(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + containsOnly( + customNoticeEntry, + *manifestEntries, + ) + getContent(customNoticeEntry).isEqualTo( + """ + Copyright 2006-2025 The Apache Software Foundation + + This product includes software developed at + The Apache Software Foundation (https://www.apache.org/). + + Notice from A + + Notice from B + """.trimIndent(), + ) + } + } + @ParameterizedTest @MethodSource("transformerConfigProvider") fun otherTransformers(pair: Pair>) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index b142ff271..95e04b0e0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -76,6 +76,14 @@ public open class ApacheNoticeResourceTransformer( @get:Input public open val charsetName: Property = objectFactory.property(Charsets.UTF_8.name()) + /** + * The output path of the `NOTICE` file. + * + * Defaults to `META-INF/NOTICE`. + */ + @get:Input + public open val outputPath: Property = objectFactory.property(NOTICE_PATH) + @Inject public constructor(objectFactory: ObjectFactory) : this( objectFactory, @@ -190,7 +198,7 @@ public open class ApacheNoticeResourceTransformer( } } - os.putNextEntry(zipEntry(NOTICE_PATH, preserveFileTimestamps)) + os.putNextEntry(zipEntry(outputPath.get(), preserveFileTimestamps)) os.write(sb.toString().trim().toByteArray(charset)) os.closeEntry() From ff3524c2e2ac0e1c910df719afc52d2f38862072 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 17 Nov 2025 17:53:01 +0800 Subject: [PATCH 855/941] Directly call setIncludes (#1852) --- .../plugins/shadow/transformers/ServiceFileTransformer.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 88bf1ce5f..616603e2b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -33,7 +33,8 @@ public open class ServiceFileTransformer @JvmOverloads constructor( public open var path: String = SERVICES_PATH set(value) { field = value - patternSet.setIncludes(listOf("$value/**")) + // Reset the includes to match the new path. + setIncludes(listOf("$value/**")) } override fun transform(context: TransformerContext) { From 5a785c266e05a83f3f2e2c452435c22894c125f8 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Mon, 17 Nov 2025 13:13:37 +0100 Subject: [PATCH 856/941] Cache patternSet.asSpec by lazy (#1854) * Add lazy for `PatternSet.asSpec` This prevents some unnecessary allocations during runtime. * add missing annotation, adapt tests * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt * Update src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt --------- Co-authored-by: Zongle Wang --- .../transformers/PatternFilterableResourceTransformer.kt | 8 +++++++- .../transformers/ApacheLicenseResourceTransformerTest.kt | 2 -- .../transformers/ApacheNoticeResourceTransformerTest.kt | 2 -- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt index 466b69996..1d9b53af2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import org.gradle.api.file.FileTreeElement +import org.gradle.api.specs.Spec import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal import org.gradle.api.tasks.util.PatternFilterable @@ -16,8 +17,13 @@ public abstract class PatternFilterableResourceTransformer( ) : ResourceTransformer by ResourceTransformer.Companion, PatternFilterable by patternSet { + private val patternSpec by lazy(LazyThreadSafetyMode.NONE) { + // Cache the spec to prevent some unnecessary allocations during runtime. + patternSet.asSpec + } + override fun canTransformResource(element: FileTreeElement): Boolean { - return patternSet.asSpec.isSatisfiedBy(element) + return patternSpec.isSatisfiedBy(element) } @Input // Trigger task executions after includes changed. diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt index fc27f7fc8..d3b292e30 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt @@ -26,8 +26,6 @@ class ApacheLicenseResourceTransformerTest : BaseTransformerTest Date: Mon, 17 Nov 2025 15:25:00 +0100 Subject: [PATCH 857/941] Add pattern-filterable to PreserveFirstFoundResourceTransformer (#1855) Co-authored-by: Zongle Wang Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 8 +++---- docs/changes/README.md | 2 ++ .../PatternFilterableResourceTransformer.kt | 3 ++- .../PreserveFirstFoundResourceTransformer.kt | 22 ++++++++++++++----- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index ed46f45a4..e6154e320 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -394,6 +394,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/transformers/Pa public fun getName ()Ljava/lang/String; public fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public final fun getPatternSet ()Lorg/gradle/api/tasks/util/PatternSet; + protected final fun getPatternSpec ()Lorg/gradle/api/specs/Spec; public fun hasTransformedResource ()Z public fun include (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; public fun include (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; @@ -405,16 +406,13 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/transformers/Pa public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } -public class com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { +public class com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/tasks/util/PatternSet;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z protected final fun getFound ()Ljava/util/Set; - public fun getName ()Ljava/lang/String; public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; public fun getResources ()Lorg/gradle/api/provider/SetProperty; - public fun hasTransformedResource ()Z - public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V - public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { diff --git a/docs/changes/README.md b/docs/changes/README.md index 487920713..8113e7a5f 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -9,6 +9,7 @@ - Expose `patternSet` of `ServiceFileTransformer` as `public`. ([#1849](https://github.com/GradleUp/shadow/pull/1849)) - Expose `patternSet` of `ApacheLicenseResourceTransformer` as `public`. ([#1850](https://github.com/GradleUp/shadow/pull/1850)) - Expose `patternSet` of `ApacheNoticeResourceTransformer` as `public`. ([#1850](https://github.com/GradleUp/shadow/pull/1850)) +- Expose `patternSet` of `PreserveFirstFoundResourceTransformer` as `public`. ([#1855](https://github.com/GradleUp/shadow/pull/1855)) - Support overriding output path of `ApacheNoticeResourceTransformer`. ([#1851](https://github.com/GradleUp/shadow/pull/1851)) ### Changed @@ -16,6 +17,7 @@ - Change the group of `startShadowScripts` from `application` to `other`. ([#1797](https://github.com/GradleUp/shadow/pull/1797)) - Update ASM and jdependency to support Java 26. ([#1799](https://github.com/GradleUp/shadow/pull/1799)) - Bump min Gradle requirement to 9.0.0. ([#1801](https://github.com/GradleUp/shadow/pull/1801)) +- Deprecate `PreserveFirstFoundResourceTransformer.resources`. ([#1855](https://github.com/GradleUp/shadow/pull/1855)) ### Fixed diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt index 1d9b53af2..beaee623c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt @@ -17,7 +17,8 @@ public abstract class PatternFilterableResourceTransformer( ) : ResourceTransformer by ResourceTransformer.Companion, PatternFilterable by patternSet { - private val patternSpec by lazy(LazyThreadSafetyMode.NONE) { + @get:Internal + protected val patternSpec: Spec by lazy(LazyThreadSafetyMode.NONE) { // Cache the spec to prevent some unnecessary allocations during runtime. patternSet.asSpec } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt index f031d3492..06d0e6a1b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt @@ -9,29 +9,41 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.util.PatternSet /** * A resource processor that preserves the first resource matched and excludes all others. * * This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and * want to ensure that only the first found resource is included in the final JAR. If there are multiple resources with - * the same path in a project and its dependencies, the first one found should be the projects'. + * the same path in a project and its dependencies, the first one found should be the project's. * * @see [DuplicatesStrategy] * @see [ShadowJar.getDuplicatesStrategy] */ @CacheableTransformer -public open class PreserveFirstFoundResourceTransformer @Inject constructor( +public open class PreserveFirstFoundResourceTransformer( final override val objectFactory: ObjectFactory, -) : ResourceTransformer by ResourceTransformer.Companion { + patternSet: PatternSet, +) : PatternFilterableResourceTransformer(patternSet) { + private val includeResources by lazy(LazyThreadSafetyMode.NONE) { + @Suppress("DEPRECATION") + include(resources.get()) + } + @get:Internal protected val found: MutableSet = mutableSetOf() + @get:Deprecated("Use `include(..)` instead") @get:Input public open val resources: SetProperty = objectFactory.setProperty() + @Inject + public constructor(objectFactory: ObjectFactory) : this(objectFactory, PatternSet()) + override fun canTransformResource(element: FileTreeElement): Boolean { - val path = element.path - return resources.get().contains(path) && !found.add(path) + // Init once before patternSpec is accessed. + includeResources + return patternSpec.isSatisfiedBy(element) && !found.add(element.path) } } From b248951cf6a0985795ecf93fd6f4cd8061b7ae86 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 07:04:59 +0800 Subject: [PATCH 858/941] Update Gradle to v9.2.1 (#1862) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bad7c2462..23449a2b5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From b332ac6038357f6e78a708bdce0856b126c2dc90 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 18 Nov 2025 10:56:45 +0800 Subject: [PATCH 859/941] Add unsafeLazy (#1863) --- .../github/jengelman/gradle/plugins/shadow/internal/Utils.kt | 3 +++ .../transformers/PatternFilterableResourceTransformer.kt | 3 ++- .../transformers/PreserveFirstFoundResourceTransformer.kt | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 708c05efd..2845bc1ea 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -29,6 +29,9 @@ internal val multiReleaseAttributeKey = JarAttributeName.MULTI_RELEASE.toString( */ internal inline fun Any?.cast(): T = this as T +@Suppress("NOTHING_TO_INLINE") +internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy = lazy(LazyThreadSafetyMode.NONE, initializer) + internal inline fun zipEntry( name: String, preserveLastModified: Boolean = true, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt index beaee623c..17945fd8c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers +import com.github.jengelman.gradle.plugins.shadow.internal.unsafeLazy import org.gradle.api.file.FileTreeElement import org.gradle.api.specs.Spec import org.gradle.api.tasks.Input @@ -18,7 +19,7 @@ public abstract class PatternFilterableResourceTransformer( PatternFilterable by patternSet { @get:Internal - protected val patternSpec: Spec by lazy(LazyThreadSafetyMode.NONE) { + protected val patternSpec: Spec by unsafeLazy { // Cache the spec to prevent some unnecessary allocations during runtime. patternSet.asSpec } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt index 06d0e6a1b..e0aaaa825 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.setProperty +import com.github.jengelman.gradle.plugins.shadow.internal.unsafeLazy import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import javax.inject.Inject import org.gradle.api.file.DuplicatesStrategy @@ -26,7 +27,7 @@ public open class PreserveFirstFoundResourceTransformer( final override val objectFactory: ObjectFactory, patternSet: PatternSet, ) : PatternFilterableResourceTransformer(patternSet) { - private val includeResources by lazy(LazyThreadSafetyMode.NONE) { + private val includeResources by unsafeLazy { @Suppress("DEPRECATION") include(resources.get()) } From aa386ebdca3723dad108aa4822b33a9a232f54cd Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Tue, 18 Nov 2025 09:25:56 +0100 Subject: [PATCH 860/941] Add new merge strategy Fail to PropertiesFileTransformer (#1856) Co-authored-by: Zongle Wang --- api/shadow.api | 1 + docs/changes/README.md | 1 + .../PropertiesFileTransformerTest.kt | 30 ++++++++++++++----- .../transformers/PropertiesFileTransformer.kt | 19 ++++++++++++ .../PropertiesFileTransformerTest.kt | 15 ++++++++++ 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index e6154e320..52c6fdbd0 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -434,6 +434,7 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesF public final class com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy : java/lang/Enum { public static final field Append Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy$Companion; + public static final field Fail Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; public static final field First Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; public static final field Latest Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; public static final fun from (Ljava/lang/String;)Lcom/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer$MergeStrategy; diff --git a/docs/changes/README.md b/docs/changes/README.md index 8113e7a5f..7fa5965ee 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -11,6 +11,7 @@ - Expose `patternSet` of `ApacheNoticeResourceTransformer` as `public`. ([#1850](https://github.com/GradleUp/shadow/pull/1850)) - Expose `patternSet` of `PreserveFirstFoundResourceTransformer` as `public`. ([#1855](https://github.com/GradleUp/shadow/pull/1855)) - Support overriding output path of `ApacheNoticeResourceTransformer`. ([#1851](https://github.com/GradleUp/shadow/pull/1851)) +- Add new merge strategy `Fail` to `PropertiesFileTransformer`. ([#1856](https://github.com/GradleUp/shadow/pull/1856)) ### Changed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index b12c96175..7ea154362 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -7,6 +7,8 @@ import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import com.github.jengelman.gradle.plugins.shadow.util.Issue import kotlin.io.path.appendText +import org.gradle.testkit.runner.TaskOutcome.FAILED +import org.junit.jupiter.api.Assertions.fail import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.EnumSource @@ -33,15 +35,29 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { ), ) - runWithSuccess(shadowJarPath) + if (strategy == MergeStrategy.Fail) { + val result = runWithFailure(shadowJarPath) - val expected = when (strategy) { - MergeStrategy.First -> arrayOf("key1=one", "key2=one", "key3=two") - MergeStrategy.Latest -> arrayOf("key1=one", "key2=two", "key3=two") - MergeStrategy.Append -> arrayOf("key1=one", "key2=one;two", "key3=two") + assertThat(result).taskOutcomeEquals(shadowJarPath, FAILED) + assertThat(result.output.replace(lineSeparator, "\n")).contains( + """ + Caused by: java.lang.IllegalStateException: The following properties files have conflicting property values and cannot be merged: + * META-INF/test.properties + * Property key2 is duplicated 2 times with different values + """.trimIndent(), + ) + } else { + runWithSuccess(shadowJarPath) + + val expected = when (strategy) { + MergeStrategy.First -> arrayOf("key1=one", "key2=one", "key3=two") + MergeStrategy.Latest -> arrayOf("key1=one", "key2=two", "key3=two") + MergeStrategy.Append -> arrayOf("key1=one", "key2=one;two", "key3=two") + else -> fail("Unexpected strategy: $strategy") + } + val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } + assertThat(content).contains(*expected) } - val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } - assertThat(content).contains(*expected) } @Test diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index aed3c054a..d938cdb1a 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -62,6 +62,8 @@ import org.gradle.api.tasks.Internal * - key2 = value2;balue2 * - key3 = value3 * + * With `mergeStrategy = MergeStrategy.Fail` the transformation will fail if there are conflicting values. + * * There are three additional properties that can be set: [paths], [mappings], * and [keyTransformer]. * The first contains a list of strings or regexes that will be used to determine if @@ -103,6 +105,9 @@ public open class PropertiesFileTransformer @Inject constructor( ) : ResourceTransformer { private inline val charset get() = Charset.forName(charsetName.get()) + @get:Internal + internal val conflicts: MutableMap> = mutableMapOf() + @get:Internal internal val propertiesEntries = mutableMapOf() @@ -156,6 +161,10 @@ public open class PropertiesFileTransformer @Inject constructor( props[key] = props.getProperty(key as String) + mergeSeparatorFor(context.path) + value } MergeStrategy.First -> Unit + MergeStrategy.Fail -> { + val conflictsForPath = conflicts.computeIfAbsent(context.path) { mutableMapOf() } + conflictsForPath.compute(key as String) { _, count -> (count ?: 1) + 1 } + } } } else { props[key] = value @@ -215,6 +224,15 @@ public open class PropertiesFileTransformer @Inject constructor( override fun hasTransformedResource(): Boolean = propertiesEntries.isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + if (conflicts.isNotEmpty()) { + val message = "The following properties files have conflicting property values and cannot be merged:" + + conflicts.map { (path, props) -> + path + props.map { "Property ${it.key} is duplicated ${it.value} times with different values" } + .joinToString(separator = "\n * ", prefix = "\n * ") + }.joinToString(separator = "\n * ", prefix = "\n * ") + error(message) + } + // Cannot close the writer as the OutputStream needs to remain open. val zipWriter = os.writer(charset) propertiesEntries.forEach { (path, props) -> @@ -231,6 +249,7 @@ public open class PropertiesFileTransformer @Inject constructor( First, Latest, Append, + Fail, ; public companion object { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index a51818829..ce8daec1f 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -64,6 +64,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest, input2: Map, expectedOutput: Map, + expectedConflicts: Map>, ) { transformer.mergeStrategy.set(MergeStrategy.from(mergeStrategy)) transformer.mergeSeparator.set(mergeSeparator) @@ -74,6 +75,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest>(), ), Arguments.of( "f.properties", @@ -271,6 +274,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest>(), ), Arguments.of( "f.properties", @@ -279,6 +283,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest>(), ), Arguments.of( "f.properties", @@ -287,6 +292,16 @@ class PropertiesFileTransformerTest : BaseTransformerTest>(), + ), + Arguments.of( + "f.properties", + "fail", + ";", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + mapOf("f.properties" to mapOf("foo" to 2)), ), ) From 14242907e8e19f87f7090fb987640673f203fb16 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 18 Nov 2025 16:48:45 +0800 Subject: [PATCH 861/941] Add invariantEolString for String (#1864) Refs https://kotlinlang.org/api/core/kotlin-stdlib/kotlin.io.path/invariant-separators-path-string.html --- .../shadow/transformers/PropertiesFileTransformerTest.kt | 3 ++- .../com/github/jengelman/gradle/plugins/shadow/util/Paths.kt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index 7ea154362..ae8802f31 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -6,6 +6,7 @@ import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import com.github.jengelman.gradle.plugins.shadow.util.Issue +import com.github.jengelman.gradle.plugins.shadow.util.invariantEolString import kotlin.io.path.appendText import org.gradle.testkit.runner.TaskOutcome.FAILED import org.junit.jupiter.api.Assertions.fail @@ -39,7 +40,7 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { val result = runWithFailure(shadowJarPath) assertThat(result).taskOutcomeEquals(shadowJarPath, FAILED) - assertThat(result.output.replace(lineSeparator, "\n")).contains( + assertThat(result.output.invariantEolString).contains( """ Caused by: java.lang.IllegalStateException: The following properties files have conflicting property values and cannot be merged: * META-INF/test.properties diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt index 79a2d5e5a..04448456a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt @@ -5,3 +5,5 @@ import kotlin.io.path.readText import kotlin.io.path.writeText fun Path.prependText(text: String) = writeText(text + readText()) + +val String.invariantEolString: String get() = replace(System.lineSeparator(), "\n") From 90eb38089833a1b9b7e0ff7fd6a65da663223ad8 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Tue, 18 Nov 2025 13:10:15 +0100 Subject: [PATCH 862/941] Add FindResourceInClasspath task to find resources in dependencies (#1860) Co-authored-by: Zongle Wang Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 20 ++++++ docs/changes/README.md | 1 + .../shadow/FindResourceInClasspathTest.kt | 67 +++++++++++++++++ .../gradle/plugins/shadow/util/Paths.kt | 5 ++ .../shadow/tasks/FindResourceInClasspath.kt | 71 +++++++++++++++++++ 5 files changed, 164 insertions(+) create mode 100644 src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath.kt diff --git a/api/shadow.api b/api/shadow.api index 52c6fdbd0..ecb12177c 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -176,6 +176,26 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/Dependenc public fun resolve (Lorg/gradle/api/artifacts/Configuration;)Lorg/gradle/api/file/FileCollection; } +public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath : org/gradle/api/DefaultTask, org/gradle/api/tasks/util/PatternFilterable { + public fun ()V + public fun (Lorg/gradle/api/tasks/util/PatternSet;)V + public fun exclude (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun exclude ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public final fun findResources ()V + protected abstract fun getArchiveOperations ()Lorg/gradle/api/file/ArchiveOperations; + public abstract fun getClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + public fun getExcludes ()Ljava/util/Set; + public fun getIncludes ()Ljava/util/Set; + public fun include (Lgroovy/lang/Closure;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include (Lorg/gradle/api/specs/Spec;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun include ([Ljava/lang/String;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun setExcludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; + public fun setIncludes (Ljava/lang/Iterable;)Lorg/gradle/api/tasks/util/PatternFilterable; +} + public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks/InheritManifest : org/gradle/api/java/archives/Manifest { public fun inheritFrom ([Ljava/lang/Object;)V public abstract fun inheritFrom ([Ljava/lang/Object;Lorg/gradle/api/Action;)V diff --git a/docs/changes/README.md b/docs/changes/README.md index 7fa5965ee..ccdf59322 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -12,6 +12,7 @@ - Expose `patternSet` of `PreserveFirstFoundResourceTransformer` as `public`. ([#1855](https://github.com/GradleUp/shadow/pull/1855)) - Support overriding output path of `ApacheNoticeResourceTransformer`. ([#1851](https://github.com/GradleUp/shadow/pull/1851)) - Add new merge strategy `Fail` to `PropertiesFileTransformer`. ([#1856](https://github.com/GradleUp/shadow/pull/1856)) +- Add `FindResourceInClasspath` task to help with debugging issues with merged duplicate resources. ([#1860](https://github.com/GradleUp/shadow/pull/1860)) ### Changed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt new file mode 100644 index 000000000..888542af7 --- /dev/null +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt @@ -0,0 +1,67 @@ +package com.github.jengelman.gradle.plugins.shadow + +import assertk.all +import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.doesNotContain +import com.github.jengelman.gradle.plugins.shadow.tasks.FindResourceInClasspath +import com.github.jengelman.gradle.plugins.shadow.util.variantSeparatorsPathString +import kotlin.io.path.appendText +import org.junit.jupiter.api.Test + +class FindResourceInClasspathTest : BasePluginTest() { + @Test + fun findResourceInClasspath() { + val taskClassName = FindResourceInClasspath::class.java.name + projectScript.appendText( + """ + dependencies { + implementation 'my:a:1.0' + } + tasks.register('find1', $taskClassName) { + classpath = configurations.runtimeClasspath + } + tasks.register('find2', $taskClassName) { + classpath = configurations.runtimeClasspath + include("a.properties") + } + tasks.register('find3', $taskClassName) { + classpath = configurations.runtimeClasspath + exclude("a.properties") + } + """.trimIndent(), + ) + + assertThat(runWithSuccess(":find1").output).contains( + "> Task :find1", + "scanning ", + "/my/a/1.0/a-1.0.jar".variantSeparatorsPathString, + "/a.properties".variantSeparatorsPathString, + "/a2.properties".variantSeparatorsPathString, + ) + + assertThat(runWithSuccess(":find2").output).all { + contains( + "> Task :find2", + "scanning ", + "/my/a/1.0/a-1.0.jar".variantSeparatorsPathString, + "/a.properties".variantSeparatorsPathString, + ) + doesNotContain( + "/a2.properties".variantSeparatorsPathString, + ) + } + + assertThat(runWithSuccess(":find3").output).all { + contains( + "> Task :find3", + "scanning ", + "/my/a/1.0/a-1.0.jar".variantSeparatorsPathString, + "/a2.properties".variantSeparatorsPathString, + ) + doesNotContain( + "/a.properties".variantSeparatorsPathString, + ) + } + } +} diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt index 04448456a..6860d4c50 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt @@ -1,5 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.util +import java.nio.file.FileSystems import java.nio.file.Path import kotlin.io.path.readText import kotlin.io.path.writeText @@ -7,3 +8,7 @@ import kotlin.io.path.writeText fun Path.prependText(text: String) = writeText(text + readText()) val String.invariantEolString: String get() = replace(System.lineSeparator(), "\n") + +val String.variantSeparatorsPathString: String get() = replace("/", fileSystem.separator) + +private val fileSystem = FileSystems.getDefault() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath.kt new file mode 100644 index 000000000..3fc2fe0a1 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath.kt @@ -0,0 +1,71 @@ +package com.github.jengelman.gradle.plugins.shadow.tasks + +import javax.inject.Inject +import org.gradle.api.DefaultTask +import org.gradle.api.file.ArchiveOperations +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.tasks.Classpath +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.TaskAction +import org.gradle.api.tasks.util.PatternFilterable +import org.gradle.api.tasks.util.PatternSet +import org.gradle.work.DisableCachingByDefault + +/** + * Helper task to temporarily add to your build script to find resources in the classpath that were + * identified as duplicates by [com.github.jengelman.gradle.plugins.shadow.transformers.MergePropertiesResourceTransformer] or + * [com.github.jengelman.gradle.plugins.shadow.transformers.DeduplicatingResourceTransformer]. + * + * First, add the task to your build script: + * + * ```kotlin + * val findResources by tasks.registering(FindResourceInClasspath::class) { + * // add configurations to search for resources in dependency jars + * classpath.from(configurations.runtimeClasspath) + * // the patterns to search for (it is a Gradle PatternFilterable) + * include( + * "META-INF/...", + * ) + * } + * ``` + * + * Then let `shadowJar` depend on the task, or just run the above task manually. + * + * ```kotlin + * tasks.shadowJar { + * dependsOn(findResources) + * } + * ``` + */ +@DisableCachingByDefault(because = "Not worth caching") +public abstract class FindResourceInClasspath(private val patternSet: PatternSet) : + DefaultTask(), + PatternFilterable by patternSet { + + @Inject public constructor() : this(PatternSet()) + + @get:InputFiles + @get:Classpath + public abstract val classpath: ConfigurableFileCollection + + @Input // Trigger task executions after includes changed. + override fun getIncludes(): MutableSet = patternSet.includes + + @Input // Trigger task executions after excludes changed. + override fun getExcludes(): MutableSet = patternSet.excludes + + @get:Inject + protected abstract val archiveOperations: ArchiveOperations + + @TaskAction + public fun findResources() { + classpath.forEach { file -> + logger.lifecycle("scanning {}", file) + + archiveOperations.zipTree(file).matching(patternSet).forEach { entry -> + logger.lifecycle(" -> {}", entry) + } + } + } +} From d359a99fd86a71911560df4d398a44b2a3618643 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 18 Nov 2025 12:22:04 +0000 Subject: [PATCH 863/941] Update kotlin monorepo to v2.3.0-RC (#1865) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c0925c442..efc9c3a2e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] jdkRelease = "17" minGradle = "9.0.0" -kotlin = "2.3.0-Beta2" +kotlin = "2.3.0-RC" moshi = "1.15.2" pluginPublish = "2.0.0" From 57096c809eb9360dc2d46c0e1f6caf0926f4e1ef Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 19 Nov 2025 02:00:53 +0000 Subject: [PATCH 864/941] Update plugin spotless to v8.1.0 (#1867) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index efc9c3a2e..5225849c3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -40,4 +40,4 @@ android-lint = "com.android.lint:8.13.1" jetbrains-dokka = "org.jetbrains.dokka:2.1.0" mavenPublish = "com.vanniktech.maven.publish:0.35.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } -spotless = "com.diffplug.spotless:8.0.0" +spotless = "com.diffplug.spotless:8.1.0" From 90b0b3e8e0fd23d62cd1900b7d470cc8dc1f103d Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Wed, 19 Nov 2025 08:44:53 +0100 Subject: [PATCH 865/941] Make the output of PropertiesFileTransformer reproducible (#1861) * `PropertiesFileTransformer` - make merged properties reproducible `PropertiesFileTransformer` leverages `java.util.Properties`, which relies on `java.util.Hashtable`. The serialized properties are not guaranteed to be reproducible. This change changes the transformer to use a sorted map to generate reproducible output. The existing class `CleanProperties` is replaced with `ReproducibleProperties`. Functionality around charset handling is retained, and extended with a functionality to generate unicode escapes (ASCII output). * Cleanups --------- Co-authored-by: Goooler --- docs/changes/README.md | 1 + .../shadow/FindResourceInClasspathTest.kt | 2 +- .../PropertiesFileTransformerTest.kt | 30 +++++-- .../gradle/plugins/shadow/util/Paths.kt | 7 -- .../shadow/internal/CleanProperties.kt | 41 +++++---- .../transformers/PropertiesFileTransformer.kt | 68 +++++--------- .../shadow/internal/CleanPropertiesTest.kt | 89 +++++++++++++++++++ .../gradle/plugins/shadow/testkit/Strings.kt | 9 ++ 8 files changed, 172 insertions(+), 75 deletions(-) create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanPropertiesTest.kt create mode 100644 src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Strings.kt diff --git a/docs/changes/README.md b/docs/changes/README.md index ccdf59322..40820254a 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -20,6 +20,7 @@ - Update ASM and jdependency to support Java 26. ([#1799](https://github.com/GradleUp/shadow/pull/1799)) - Bump min Gradle requirement to 9.0.0. ([#1801](https://github.com/GradleUp/shadow/pull/1801)) - Deprecate `PreserveFirstFoundResourceTransformer.resources`. ([#1855](https://github.com/GradleUp/shadow/pull/1855)) +- Make the output of `PropertiesFileTransformer` reproducible. ([#1861](https://github.com/GradleUp/shadow/pull/1861)) ### Fixed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt index 888542af7..115d940c1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt @@ -5,7 +5,7 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.doesNotContain import com.github.jengelman.gradle.plugins.shadow.tasks.FindResourceInClasspath -import com.github.jengelman.gradle.plugins.shadow.util.variantSeparatorsPathString +import com.github.jengelman.gradle.plugins.shadow.testkit.variantSeparatorsPathString import kotlin.io.path.appendText import org.junit.jupiter.api.Test diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index ae8802f31..d77911e7a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -4,9 +4,9 @@ import assertk.assertThat import assertk.assertions.contains import assertk.assertions.isEqualTo import com.github.jengelman.gradle.plugins.shadow.testkit.getContent +import com.github.jengelman.gradle.plugins.shadow.testkit.invariantEolString import com.github.jengelman.gradle.plugins.shadow.transformers.PropertiesFileTransformer.MergeStrategy import com.github.jengelman.gradle.plugins.shadow.util.Issue -import com.github.jengelman.gradle.plugins.shadow.util.invariantEolString import kotlin.io.path.appendText import org.gradle.testkit.runner.TaskOutcome.FAILED import org.junit.jupiter.api.Assertions.fail @@ -51,13 +51,31 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { runWithSuccess(shadowJarPath) val expected = when (strategy) { - MergeStrategy.First -> arrayOf("key1=one", "key2=one", "key3=two") - MergeStrategy.Latest -> arrayOf("key1=one", "key2=two", "key3=two") - MergeStrategy.Append -> arrayOf("key1=one", "key2=one;two", "key3=two") + MergeStrategy.First -> + """ + |key1=one + |key2=one + |key3=two + | + """.trimMargin() + MergeStrategy.Latest -> + """ + |key1=one + |key2=two + |key3=two + | + """.trimMargin() + MergeStrategy.Append -> + """ + |key1=one + |key2=one;two + |key3=two + | + """.trimMargin() else -> fail("Unexpected strategy: $strategy") } val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } - assertThat(content).contains(*expected) + assertThat(content.invariantEolString).isEqualTo(expected) } } @@ -168,8 +186,6 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } assertThat(content.trimIndent()).isEqualTo( """ - # - foo=one,two """.trimIndent(), ) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt index 6860d4c50..79a2d5e5a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Paths.kt @@ -1,14 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.util -import java.nio.file.FileSystems import java.nio.file.Path import kotlin.io.path.readText import kotlin.io.path.writeText fun Path.prependText(text: String) = writeText(text + readText()) - -val String.invariantEolString: String get() = replace(System.lineSeparator(), "\n") - -val String.variantSeparatorsPathString: String get() = replace("/", fileSystem.separator) - -private val fileSystem = FileSystems.getDefault() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt index 624aa422d..87ac366dc 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt @@ -1,31 +1,40 @@ package com.github.jengelman.gradle.plugins.shadow.internal -import java.io.BufferedWriter -import java.io.IOException +import java.io.OutputStream +import java.io.StringWriter import java.io.Writer -import java.util.Date +import java.nio.charset.Charset import java.util.Properties /** - * Introduced in order to remove prepended timestamp when creating output stream. + * Provides functionality for reproducible serialization. */ internal class CleanProperties : Properties() { - @Throws(IOException::class) override fun store(writer: Writer, comments: String) { - super.store(StripCommentsWithTimestampBufferedWriter(writer), comments) + throw UnsupportedOperationException("use writeWithoutComments()") } - private class StripCommentsWithTimestampBufferedWriter(out: Writer) : BufferedWriter(out) { - private val lengthOfExpectedTimestamp = ("#" + Date().toString()).length + override fun store(out: OutputStream, comments: String?) { + throw UnsupportedOperationException("use writeWithoutComments()") + } - @Throws(IOException::class) - override fun write(str: String) { - if (str.couldBeCommentWithTimestamp) return - super.write(str) - } + fun writeWithoutComments(charset: Charset, os: OutputStream) { + val bufferedReader = StringWriter().apply { + super.store(this, null) + }.toString().reader().buffered() - private val String?.couldBeCommentWithTimestamp: Boolean get() { - return this != null && startsWith("#") && length == lengthOfExpectedTimestamp - } + os.bufferedWriter(charset).apply { + var line: String? = null + while (bufferedReader.readLine().also { line = it } != null && line != null) { + if (!line.startsWith("#")) { + write(line) + newLine() + } + } + }.flush() } + + override val entries: MutableSet> + // Yields the entries for Properties.store0() in sorted order. + get() = super.entries.toSortedSet(compareBy { it.key.toString() }) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index d938cdb1a..45d7aab58 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -1,7 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties -import com.github.jengelman.gradle.plugins.shadow.internal.inputStream import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.setProperty @@ -123,6 +122,10 @@ public open class PropertiesFileTransformer @Inject constructor( @get:Input public open val mergeSeparator: Property = objectFactory.property(",") + /** + * The character set to use when reading and writing property files. + * Defaults to `ISO-8859-1`. + */ @get:Input public open val charsetName: Property = objectFactory.property(Charsets.ISO_8859_1.name()) @@ -146,49 +149,31 @@ public open class PropertiesFileTransformer @Inject constructor( } override fun transform(context: TransformerContext) { - val props = propertiesEntries[context.path] - val incoming = loadAndTransformKeys(context.inputStream) - if (props == null) { - propertiesEntries[context.path] = incoming - } else { - for ((key, value) in incoming) { - if (props.containsKey(key)) { - when (MergeStrategy.from(mergeStrategyFor(context.path))) { - MergeStrategy.Latest -> { - props[key] = value - } - MergeStrategy.Append -> { - props[key] = props.getProperty(key as String) + mergeSeparatorFor(context.path) + value - } - MergeStrategy.First -> Unit - MergeStrategy.Fail -> { - val conflictsForPath = conflicts.computeIfAbsent(context.path) { mutableMapOf() } - conflictsForPath.compute(key as String) { _, count -> (count ?: 1) + 1 } - } + val props = propertiesEntries.computeIfAbsent(context.path) { CleanProperties() } + loadAndTransformKeys(context.inputStream) { key, value -> + if (props.containsKey(key)) { + when (MergeStrategy.from(mergeStrategyFor(context.path))) { + MergeStrategy.Latest -> { + props[key] = value + } + MergeStrategy.Append -> { + props[key] = props[key] as String + mergeSeparatorFor(context.path) + value + } + MergeStrategy.First -> Unit + MergeStrategy.Fail -> { + val conflictsForPath = conflicts.computeIfAbsent(context.path) { mutableMapOf() } + conflictsForPath.compute(key) { _, count -> (count ?: 1) + 1 } } - } else { - props[key] = value } + } else { + props[key] = value } } } - private fun loadAndTransformKeys(inputStream: InputStream): CleanProperties { - val props = CleanProperties() - // InputStream closed by caller, so we don't do it here. - props.load(inputStream.bufferedReader(charset)) - return transformKeys(props) - } - - private fun transformKeys(properties: Properties): CleanProperties { - if (keyTransformer == IDENTITY) { - return properties as CleanProperties - } - val result = CleanProperties() - properties.forEach { (key, value) -> - result[keyTransformer(key as String)] = value - } - return result + private fun loadAndTransformKeys(inputStream: InputStream, action: (key: String, value: String) -> Unit) { + val props = Properties().apply { load(inputStream.bufferedReader(charset)) } + props.forEach { action(keyTransformer(it.key as String), it.value as String) } } private fun mergeStrategyFor(path: String): String { @@ -233,14 +218,9 @@ public open class PropertiesFileTransformer @Inject constructor( error(message) } - // Cannot close the writer as the OutputStream needs to remain open. - val zipWriter = os.writer(charset) propertiesEntries.forEach { (path, props) -> os.putNextEntry(zipEntry(path, preserveFileTimestamps)) - props.inputStream(charset).bufferedReader(charset).use { - it.copyTo(zipWriter) - } - zipWriter.flush() + props.writeWithoutComments(charset, os) os.closeEntry() } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanPropertiesTest.kt new file mode 100644 index 000000000..be5c5bd58 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanPropertiesTest.kt @@ -0,0 +1,89 @@ +package com.github.jengelman.gradle.plugins.shadow.internal + +import assertk.assertThat +import assertk.assertions.isEqualTo +import com.github.jengelman.gradle.plugins.shadow.testkit.invariantEolString +import java.io.ByteArrayOutputStream +import java.nio.charset.Charset +import java.nio.charset.StandardCharsets +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +class CleanPropertiesTest { + @ParameterizedTest + @MethodSource("generalCharsetsProvider") + fun emptyProperties(charset: Charset) { + val output = CleanProperties().writeToString(charset) + + assertThat(output).isEqualTo("") + } + + @ParameterizedTest + @MethodSource("generalCharsetsProvider") + fun asciiProps(charset: Charset) { + val output = CleanProperties().also { props -> + props["key"] = "value" + props["key2"] = "value2" + props["a"] = "b" + props["d"] = "e" + props["0"] = "1" + props["b"] = "c" + props["c"] = "d" + props["e"] = "f" + }.writeToString(charset) + + assertThat(output).isEqualTo( + """ + |0=1 + |a=b + |b=c + |c=d + |d=e + |e=f + |key=value + |key2=value2 + | + """.trimMargin(), + ) + } + + @ParameterizedTest + @MethodSource("utfCharsetsProvider") + fun utfProps(charset: Charset) { + val output = CleanProperties().also { props -> + props["äöüß"] = "aouss" + props["áèô"] = "aeo" + props["€²³"] = "x" + props["传傳磨宿说説"] = "b" + }.writeToString(charset) + + assertThat(output).isEqualTo( + """ + |áèô=aeo + |äöüß=aouss + |€²³=x + |传傳磨宿说説=b + | + """.trimMargin(), + ) + } + + private companion object Companion { + @JvmStatic + fun generalCharsetsProvider() = listOf( + StandardCharsets.ISO_8859_1, + StandardCharsets.US_ASCII, + ) + utfCharsetsProvider() + + @JvmStatic + fun utfCharsetsProvider() = listOf( + StandardCharsets.UTF_8, + StandardCharsets.UTF_16, + ) + + fun CleanProperties.writeToString(charset: Charset): String { + return ByteArrayOutputStream().also { writeWithoutComments(charset, it) } + .toString(charset.name()).invariantEolString + } + } +} diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Strings.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Strings.kt new file mode 100644 index 000000000..8dac7b1dd --- /dev/null +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Strings.kt @@ -0,0 +1,9 @@ +package com.github.jengelman.gradle.plugins.shadow.testkit + +import java.nio.file.FileSystems + +val String.invariantEolString: String get() = replace(System.lineSeparator(), "\n") + +val String.variantSeparatorsPathString: String get() = replace("/", fileSystem.separator) + +private val fileSystem = FileSystems.getDefault() From edcba4ef5aaabf9a60624f00d305f711710b0e7a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 19 Nov 2025 16:13:23 +0800 Subject: [PATCH 866/941] Test merged properties without comments (#1868) --- .../PropertiesFileTransformerTest.kt | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index d77911e7a..45f3287a3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -160,16 +160,27 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { } @Issue( - "https://github.com/GradleUp/shadow/issues/622", "https://github.com/GradleUp/shadow/issues/856", ) @Test - fun mergedPropertiesDoNotContainDateComment() { + fun mergedPropertiesWithoutComments() { val one = buildJarOne { - insert("META-INF/test.properties", "foo=one") + insert( + "META-INF/test.properties", + """ + # A comment from jar one. + foo=one + """.trimIndent(), + ) } val two = buildJarTwo { - insert("META-INF/test.properties", "foo=two") + insert( + "META-INF/test.properties", + """ + # A comment from jar two. + foo=two + """.trimIndent(), + ) } projectScript.appendText( transform( @@ -184,10 +195,11 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } - assertThat(content.trimIndent()).isEqualTo( + assertThat(content.invariantEolString).isEqualTo( """ - foo=one,two - """.trimIndent(), + |foo=one,two + | + """.trimMargin(), ) } From 3baee9a30f8afe71cd285bdb1c23d4d3ee8ea66d Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 19 Nov 2025 16:24:46 +0800 Subject: [PATCH 867/941] Rename CleanProperties to ReproducibleProperties (#1869) --- .../{CleanProperties.kt => ReproducibleProperties.kt} | 2 +- .../shadow/transformers/PropertiesFileTransformer.kt | 6 +++--- ...PropertiesTest.kt => ReproduciblePropertiesTest.kt} | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) rename src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/{CleanProperties.kt => ReproducibleProperties.kt} (95%) rename src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/{CleanPropertiesTest.kt => ReproduciblePropertiesTest.kt} (87%) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproducibleProperties.kt similarity index 95% rename from src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt rename to src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproducibleProperties.kt index 87ac366dc..02c3a3097 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanProperties.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproducibleProperties.kt @@ -9,7 +9,7 @@ import java.util.Properties /** * Provides functionality for reproducible serialization. */ -internal class CleanProperties : Properties() { +internal class ReproducibleProperties : Properties() { override fun store(writer: Writer, comments: String) { throw UnsupportedOperationException("use writeWithoutComments()") } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 45d7aab58..92aeadc21 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -1,6 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.transformers -import com.github.jengelman.gradle.plugins.shadow.internal.CleanProperties +import com.github.jengelman.gradle.plugins.shadow.internal.ReproducibleProperties import com.github.jengelman.gradle.plugins.shadow.internal.mapProperty import com.github.jengelman.gradle.plugins.shadow.internal.property import com.github.jengelman.gradle.plugins.shadow.internal.setProperty @@ -108,7 +108,7 @@ public open class PropertiesFileTransformer @Inject constructor( internal val conflicts: MutableMap> = mutableMapOf() @get:Internal - internal val propertiesEntries = mutableMapOf() + internal val propertiesEntries = mutableMapOf() @get:Input public open val paths: SetProperty = objectFactory.setProperty() @@ -149,7 +149,7 @@ public open class PropertiesFileTransformer @Inject constructor( } override fun transform(context: TransformerContext) { - val props = propertiesEntries.computeIfAbsent(context.path) { CleanProperties() } + val props = propertiesEntries.computeIfAbsent(context.path) { ReproducibleProperties() } loadAndTransformKeys(context.inputStream) { key, value -> if (props.containsKey(key)) { when (MergeStrategy.from(mergeStrategyFor(context.path))) { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproduciblePropertiesTest.kt similarity index 87% rename from src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanPropertiesTest.kt rename to src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproduciblePropertiesTest.kt index be5c5bd58..e0775596a 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/CleanPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproduciblePropertiesTest.kt @@ -9,11 +9,11 @@ import java.nio.charset.StandardCharsets import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource -class CleanPropertiesTest { +class ReproduciblePropertiesTest { @ParameterizedTest @MethodSource("generalCharsetsProvider") fun emptyProperties(charset: Charset) { - val output = CleanProperties().writeToString(charset) + val output = ReproducibleProperties().writeToString(charset) assertThat(output).isEqualTo("") } @@ -21,7 +21,7 @@ class CleanPropertiesTest { @ParameterizedTest @MethodSource("generalCharsetsProvider") fun asciiProps(charset: Charset) { - val output = CleanProperties().also { props -> + val output = ReproducibleProperties().also { props -> props["key"] = "value" props["key2"] = "value2" props["a"] = "b" @@ -50,7 +50,7 @@ class CleanPropertiesTest { @ParameterizedTest @MethodSource("utfCharsetsProvider") fun utfProps(charset: Charset) { - val output = CleanProperties().also { props -> + val output = ReproducibleProperties().also { props -> props["äöüß"] = "aouss" props["áèô"] = "aeo" props["€²³"] = "x" @@ -81,7 +81,7 @@ class CleanPropertiesTest { StandardCharsets.UTF_16, ) - fun CleanProperties.writeToString(charset: Charset): String { + fun ReproducibleProperties.writeToString(charset: Charset): String { return ByteArrayOutputStream().also { writeWithoutComments(charset, it) } .toString(charset.name()).invariantEolString } From ffca329f9bbd682a161d82a9b1e8ed27563b74f2 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Wed, 19 Nov 2025 14:52:58 +0100 Subject: [PATCH 868/941] Add MergeLicenseResourceTransformer to merge licenses (#1858) Co-authored-by: Zongle Wang Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- api/shadow.api | 14 ++ docs/changes/README.md | 1 + .../shadow/transformers/TransformersTest.kt | 45 ++++++ .../MergeLicenseResourceTransformer.kt | 137 ++++++++++++++++ .../MergeLicenseResourceTransformerTest.kt | 146 ++++++++++++++++++ 5 files changed, 343 insertions(+) create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformerTest.kt diff --git a/api/shadow.api b/api/shadow.api index ecb12177c..cc21666cf 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -402,6 +402,20 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ManifestRes public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V } +public class com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer { + public fun (Lorg/gradle/api/model/ObjectFactory;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/tasks/util/PatternSet;)V + public fun getArtifactLicense ()Lorg/gradle/api/file/RegularFileProperty; + public fun getArtifactLicenseSpdxId ()Lorg/gradle/api/provider/Property; + public fun getFirstSeparator ()Lorg/gradle/api/provider/Property; + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public fun getOutputPath ()Lorg/gradle/api/provider/Property; + public fun getSeparator ()Lorg/gradle/api/provider/Property; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V + public fun transform (Lcom/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext;)V +} + public abstract class com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer, org/gradle/api/tasks/util/PatternFilterable { public fun (Lorg/gradle/api/tasks/util/PatternSet;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z diff --git a/docs/changes/README.md b/docs/changes/README.md index 40820254a..23ec001fd 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -13,6 +13,7 @@ - Support overriding output path of `ApacheNoticeResourceTransformer`. ([#1851](https://github.com/GradleUp/shadow/pull/1851)) - Add new merge strategy `Fail` to `PropertiesFileTransformer`. ([#1856](https://github.com/GradleUp/shadow/pull/1856)) - Add `FindResourceInClasspath` task to help with debugging issues with merged duplicate resources. ([#1860](https://github.com/GradleUp/shadow/pull/1860)) +- Add `MergeLicenseResourceTransformer`. ([#1858](https://github.com/GradleUp/shadow/pull/1858)) ### Changed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index cba2f7ebb..9da1f572b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -10,6 +10,7 @@ import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import com.github.jengelman.gradle.plugins.shadow.testkit.getStream +import com.github.jengelman.gradle.plugins.shadow.testkit.invariantEolString import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath import com.github.jengelman.gradle.plugins.shadow.util.Issue import java.util.jar.Attributes as JarAttribute @@ -352,4 +353,48 @@ class TransformersTest : BaseTransformerTest() { "" to ManifestResourceTransformer::class, ) } + + @Test + fun mergeLicenseResourceTransformer() { + val one = buildJarOne { + insert("META-INF/LICENSE", "license one") + } + val two = buildJarTwo { + insert("META-INF/LICENSE", "license two") + } + val artifactLicense = path("my-license") + artifactLicense.writeText("artifact license text") + + projectScript.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = """ + outputPath = 'MY_LICENSE' + artifactLicense = file('${artifactLicense.invariantSeparatorsPathString}') + firstSeparator = '####' + separator = '----' + """.trimIndent(), + ), + ) + + runWithSuccess(shadowJarPath) + + assertThat(outputShadowedJar).useAll { + containsOnly( + "MY_LICENSE", + "META-INF/", + "META-INF/MANIFEST.MF", + ) + getContent("MY_LICENSE").transform { it.invariantEolString }.isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license text + #### + license one + ---- + license two + """.trimIndent(), + ) + } + } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt new file mode 100644 index 000000000..0f895c5e4 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt @@ -0,0 +1,137 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.internal.property +import com.github.jengelman.gradle.plugins.shadow.internal.zipEntry +import java.nio.charset.StandardCharsets.UTF_8 +import java.util.LinkedHashSet +import javax.inject.Inject +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.util.PatternSet + +/** + * Generates a license file using the configured license text source. + * + * An optional `SPDX-License-Identifier` can be placed in front of the license text to avoid ambiguous + * license detection by license-detection-tools. + * + * License texts found in the file names: + * + * - `META-INF/LICENSE` + * - `META-INF/LICENSE.txt` + * - `META-INF/LICENSE.md` + * - `LICENSE` + * - `LICENSE.txt` + * - `LICENSE.md` + * + * are included for the shadowed jar sources. + * + * To exclude these defaults, add [exclude]s to the transformer configuration. + * + * Use the [org.gradle.api.tasks.util.PatternFilterable] functions to specify a different set of files to include, + * the paths mentioned above are then not considered unless explicitly included. + */ +@CacheableTransformer +public open class MergeLicenseResourceTransformer( + final override val objectFactory: ObjectFactory, + patternSet: PatternSet, +) : PatternFilterableResourceTransformer(patternSet) { + @get:Internal + internal val elements: MutableSet = LinkedHashSet() + + /** Path to write the aggregated license file to. Defaults to `META-INF/LICENSE`. */ + @get:Input + public open val outputPath: Property = objectFactory.property("META-INF/LICENSE") + + /** + * The generated license file is potentially a collection of multiple license texts. To avoid + * ambiguous license detection by license-detection-tools, an SPDX license identifier header + * (`SPDX-License-Identifier:`) is added at the beginning of the generated file if the value of + * this property is present and not empty. Defaults to `Apache-2.0`. + */ + @get:Input + public open val artifactLicenseSpdxId: Property = objectFactory.property("Apache-2.0") + + /** Path to the project's license text, this property *must* be configured. */ + @get:InputFile + @get:PathSensitive(PathSensitivity.RELATIVE) + public open val artifactLicense: RegularFileProperty = objectFactory.fileProperty() + + /** + * Separator between the project's license text and license texts from the included dependencies. + */ + @get:Input + public open val firstSeparator: Property = objectFactory.property( + """ + | + |${"-".repeat(120)} + | + |This artifact includes dependencies with the following licenses: + |---------------------------------------------------------------- + | + """.trimMargin(), + ) + + /** + * Separator between included dependency license texts. + */ + @get:Input + public open val separator: Property = objectFactory.property("\n${"-".repeat(120)}\n") + + @Inject + public constructor(objectFactory: ObjectFactory) : this( + objectFactory, + patternSet = PatternSet().apply { + include( + "META-INF/LICENSE", + "META-INF/LICENSE.txt", + "META-INF/LICENSE.md", + "LICENSE", + "LICENSE.txt", + "LICENSE.md", + ) + }, + ) + + override fun transform(context: TransformerContext) { + transformInternal(context.inputStream.readAllBytes()) + } + + override fun hasTransformedResource(): Boolean = true + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + os.putNextEntry(zipEntry(outputPath.get(), preserveFileTimestamps)) + os.write(buildLicense().toByteArray()) + os.closeEntry() + } + + internal fun transformInternal(bytes: ByteArray) { + val content = bytes.toString(UTF_8).trim('\n', '\r') + if (content.isNotEmpty()) { + elements.add(content) + } + } + + internal fun buildLicense() = buildString { + val spdxId = artifactLicenseSpdxId.orNull.orEmpty() + if (spdxId.isNotBlank()) { + append("SPDX-License-Identifier: $spdxId\n") + } + + append(artifactLicense.get().asFile.readText()) + + if (elements.isNotEmpty()) { + append("\n" + firstSeparator.get() + "\n") + + val separatorLine = "\n" + separator.get() + "\n" + append(elements.joinToString(separator = separatorLine)) + } + } +} diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformerTest.kt new file mode 100644 index 000000000..ab890efe5 --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformerTest.kt @@ -0,0 +1,146 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.containsExactlyInAnyOrder +import assertk.assertions.isEmpty +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import java.nio.file.Path +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +class MergeLicenseResourceTransformerTest : BaseTransformerTest() { + @Test + fun defaultIncludes() { + with(transformer) { + assertThat(canTransformResource("META-INF/LICENSE")).isTrue() + assertThat(canTransformResource("META-INF/LICENSE.txt")).isTrue() + assertThat(canTransformResource("META-INF/LICENSE.md")).isTrue() + assertThat(canTransformResource("LICENSE")).isTrue() + assertThat(canTransformResource("LICENSE.txt")).isTrue() + assertThat(canTransformResource("LICENSE.md")).isTrue() + assertThat(canTransformResource("something else")).isFalse() + } + } + + @Test + fun customIncludes() { + with(transformer) { + include("META-INF/FOO") + exclude("META-INF/LICENSE*") + exclude("LICENSE*") + assertThat(canTransformResource("META-INF/FOO")).isTrue() + assertThat(canTransformResource("META-INF/LICENSE")).isFalse() + assertThat(canTransformResource("META-INF/LICENSE.txt")).isFalse() + assertThat(canTransformResource("META-INF/LICENSE.md")).isFalse() + assertThat(canTransformResource("LICENSE")).isFalse() + assertThat(canTransformResource("LICENSE.txt")).isFalse() + assertThat(canTransformResource("LICENSE.md")).isFalse() + assertThat(canTransformResource("something else")).isFalse() + } + } + + @Test + fun deduplicateLicenseTexts(@TempDir tempDir: Path) { + with(transformer) { + transformInternal("license one".toByteArray()) + transformInternal("\r\nlicense one\r\n".toByteArray()) + transformInternal("\nlicense one\n".toByteArray()) + transformInternal(" license two".toByteArray()) + transformInternal("\r\n\n\r\n\n license two".toByteArray()) + transformInternal(" license two\r\n\n\r\n\n".toByteArray()) + transformInternal("license three".toByteArray()) + + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) + + assertThat(elements).containsExactlyInAnyOrder("license one", " license two", "license three") + + assertThat(buildLicense()).isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license file content + + ------------------------------------------------------------------------------------------------------------------------ + + This artifact includes dependencies with the following licenses: + ---------------------------------------------------------------- + + license one + + ------------------------------------------------------------------------------------------------------------------------ + + license two + + ------------------------------------------------------------------------------------------------------------------------ + + license three + """.trimIndent(), + ) + } + } + + @Test + fun singleAdditionalLicense(@TempDir tempDir: Path) { + with(transformer) { + transformInternal("license one".toByteArray()) + + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) + + assertThat(elements).containsExactlyInAnyOrder("license one") + + assertThat(buildLicense()).isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license file content + + ------------------------------------------------------------------------------------------------------------------------ + + This artifact includes dependencies with the following licenses: + ---------------------------------------------------------------- + + license one + """.trimIndent(), + ) + } + } + + @Test + fun noAdditionalLicenses(@TempDir tempDir: Path) { + with(transformer) { + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) + + assertThat(elements).isEmpty() + + assertThat(buildLicense()).isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license file content + """.trimIndent(), + ) + } + } + + @Test + fun noSpdxId(@TempDir tempDir: Path) { + with(transformer) { + artifactLicenseSpdxId.unsetConvention() + + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) + + assertThat(elements).isEmpty() + + assertThat(buildLicense()).isEqualTo( + "artifact license file content", + ) + } + } +} From 778c39a2c575003b45a5159df0f9705f07b1bab0 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 20 Nov 2025 10:29:48 +0800 Subject: [PATCH 869/941] Tweak unit test kits (#1870) * Remove `isFile` * Remove `readContentLines` * Replace `MANIFEST_NAME` * Use `single` * Remove `protected` * Use `requireResourceAsPath` * Add `resourceContext` and `textContext` * Overload `canTransformResource` * Rename `doTransformAndGetTransformedPath` to `transformToJar` * Fix * Improve the assertion * Fewer lines * Call more `use` * Update src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Import --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../ApacheNoticeResourceTransformerTest.kt | 6 +--- .../transformers/BaseTransformerTest.kt | 32 +++++++++++-------- .../ComponentsXmlResourceTransformerTest.kt | 15 +++------ .../Log4j2PluginsCacheFileTransformerTest.kt | 3 +- .../ManifestAppenderTransformerTest.kt | 20 ++++++------ .../PropertiesFileTransformerTest.kt | 8 +++-- .../ServiceFileTransformerTest.kt | 21 +++++------- 7 files changed, 47 insertions(+), 58 deletions(-) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt index e84688d51..7f28e2582 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -43,7 +43,7 @@ class ApacheNoticeResourceTransformerTest : BaseTransformerTest { - protected lateinit var transformer: T + lateinit var transformer: T private set - protected val manifestTransformerContext: TransformerContext + val manifestTransformerContext: TransformerContext get() = TransformerContext(MANIFEST_NAME, requireResourceAsStream(MANIFEST_NAME)) @BeforeEach open fun beforeEach() { @Suppress("UNCHECKED_CAST") - val clazz = (this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments.first() as Class + val clazz = (this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments.single() as Class transformer = clazz.create(testObjectFactory) } companion object { - const val MANIFEST_NAME: String = "META-INF/MANIFEST.MF" - - fun ResourceTransformer.canTransformResource(path: String, isFile: Boolean = true): Boolean { + fun ResourceTransformer.canTransformResource(path: String, file: File? = null): Boolean { val element = object : FileTreeElement by noOpDelegate() { - private val _relativePath = RelativePath.parse(isFile, path) + private val _relativePath = RelativePath.parse(true, path) override fun getPath(): String = _relativePath.pathString override fun getRelativePath(): RelativePath = _relativePath + override fun getFile(): File = requireNotNull(file) { "File must be provided." } } return canTransformResource(element) } - fun JarPath.readContentLines(resourceName: String = MANIFEST_NAME): List { - return use { it.getStream(resourceName).bufferedReader().readLines() } + fun resourceContext(path: String, vararg relocators: Relocator): TransformerContext { + return TransformerContext(path = path, inputStream = requireResourceAsStream(path), relocators = relocators.toSet()) + } + + fun textContext(path: String, text: String = "", vararg relocators: Relocator): TransformerContext { + return TransformerContext(path = path, inputStream = text.byteInputStream(), relocators = relocators.toSet()) } - fun doTransformAndGetTransformedPath( - transformer: ResourceTransformer, - preserveFileTimestamps: Boolean, + fun ResourceTransformer.transformToJar( + preserveFileTimestamps: Boolean = true, ): JarPath { val testableZipPath = createTempFile("testable-zip-file-", ".jar") ZipOutputStream(testableZipPath.outputStream()).use { zipOutputStream -> - transformer.modifyOutputStream(zipOutputStream, preserveFileTimestamps) + modifyOutputStream(zipOutputStream, preserveFileTimestamps) } return JarPath(testableZipPath) } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt index 3abea396c..b503223b4 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -2,7 +2,8 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.assertThat import assertk.assertions.isTrue -import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsStream +import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath +import kotlin.io.path.readText import org.custommonkey.xmlunit.XMLUnit import org.junit.jupiter.api.Test @@ -13,19 +14,13 @@ class ComponentsXmlResourceTransformerTest : BaseTransformerTest() { @@ -44,27 +46,23 @@ class ManifestAppenderTransformerTest : BaseTransformerTest() @MethodSource("serviceFileProvider") fun transformServiceFile(path: String, input1: String, input2: String, output: String) { if (transformer.canTransformResource(path)) { - transformer.transform(context(path, input1)) - transformer.transform(context(path, input2)) + transformer.transform(textContext(path, input1)) + transformer.transform(textContext(path, input2)) } assertThat(transformer.hasTransformedResource()).isTrue() @@ -80,7 +79,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() val contentResourceShaded = "META-INF/services/borg.foo.something.another" val transformer = ServiceFileTransformer() - transformer.transform(context(contentResource, content, relocator)) + transformer.transform(textContext(contentResource, content, relocator)) tempJar.outputStream().zipOutputStream().use { zos -> transformer.modifyOutputStream(zos, false) @@ -98,8 +97,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() val contentResourceShaded = "META-INF/services/borg.foo.something.another" val transformer = ServiceFileTransformer() - transformer.transform(context(contentResource, content, relocator)) - transformer.transform(context(contentResourceShaded, content, relocator)) + transformer.transform(textContext(contentResource, content, relocator)) + transformer.transform(textContext(contentResourceShaded, content, relocator)) tempJar.outputStream().zipOutputStream().use { zos -> transformer.modifyOutputStream(zos, false) @@ -116,7 +115,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() val contentResource = "META-INF/services/org.osgi.framework.launch.FrameworkFactory" val transformer = ServiceFileTransformer() - transformer.transform(context(contentResource, content, relocator)) + transformer.transform(textContext(contentResource, content, relocator)) tempJar.outputStream().zipOutputStream().use { zos -> transformer.modifyOutputStream(zos, false) @@ -133,12 +132,12 @@ class ServiceFileTransformerTest : BaseTransformerTest() var contentResource = "META-INF/services/org.something.another" val transformer = ServiceFileTransformer() - transformer.transform(context(contentResource, content, relocator)) + transformer.transform(textContext(contentResource, content, relocator)) content = "org.blah.Service\n" contentResource = "META-INF/services/org.something.another" - transformer.transform(context(contentResource, content, relocator)) + transformer.transform(textContext(contentResource, content, relocator)) tempJar.outputStream().zipOutputStream().use { zos -> transformer.modifyOutputStream(zos, false) @@ -149,10 +148,6 @@ class ServiceFileTransformerTest : BaseTransformerTest() } private companion object { - fun context(path: String, input: String, vararg relocators: Relocator): TransformerContext { - return TransformerContext(path, input.byteInputStream(), relocators = relocators.toSet()) - } - @JvmStatic fun resourceProvider() = listOf( // path, exclude, expected From a78b1320f63e484d1ad2f18c0a733f3ed624b0dd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 09:10:21 +0800 Subject: [PATCH 870/941] Update actions/checkout action to v6 (#1872) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/build.yml | 4 ++-- .github/workflows/deploy.yml | 2 +- .github/workflows/links.yml | 2 +- .github/workflows/release.yml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index fbe655472..ede08b744 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: gradle: [ 9.0.0, current ] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: distribution: 'zulu' @@ -46,7 +46,7 @@ jobs: runs-on: ubuntu-24.04-arm if: github.event.repository.fork == false && github.ref == 'refs/heads/main' steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: distribution: 'zulu' diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index dad254fd6..e2c2c5685 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -14,7 +14,7 @@ jobs: id-token: write pages: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true diff --git a/.github/workflows/links.yml b/.github/workflows/links.yml index 9abad4007..e01775027 100644 --- a/.github/workflows/links.yml +++ b/.github/workflows/links.yml @@ -22,7 +22,7 @@ jobs: runs-on: ubuntu-latest # TODO: https://github.com/UmbrellaDocs/linkspector/issues/149 if: github.event.repository.fork == false steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f59796204..a42245e8b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: permissions: contents: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: actions/setup-java@v5 with: distribution: 'zulu' From ff0a30b1b88632b44e155503a61ffb4aa6b1b83a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 21 Nov 2025 10:51:20 +0800 Subject: [PATCH 871/941] Reduce nesting for using transformer assertions (#1873) --- .../ManifestAppenderTransformerTest.kt | 28 ++- .../MergeLicenseResourceTransformerTest.kt | 180 ++++++++---------- 2 files changed, 96 insertions(+), 112 deletions(-) diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt index 87575b069..3b005a9b0 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformerTest.kt @@ -13,14 +13,12 @@ import org.junit.jupiter.api.Test class ManifestAppenderTransformerTest : BaseTransformerTest() { @Test - fun canTransformResource() { - with(transformer) { - append("Name", "org/foo/bar/") - append("Sealed", true) - } + fun canTransformResource() = with(transformer) { + append("Name", "org/foo/bar/") + append("Sealed", true) - assertThat(transformer.canTransformResource(MANIFEST_NAME)).isTrue() - assertThat(transformer.canTransformResource(MANIFEST_NAME.lowercase())).isTrue() + assertThat(canTransformResource(MANIFEST_NAME)).isTrue() + assertThat(canTransformResource(MANIFEST_NAME.lowercase())).isTrue() } @Test @@ -36,17 +34,15 @@ class ManifestAppenderTransformerTest : BaseTransformerTest() { @Test - fun defaultIncludes() { - with(transformer) { - assertThat(canTransformResource("META-INF/LICENSE")).isTrue() - assertThat(canTransformResource("META-INF/LICENSE.txt")).isTrue() - assertThat(canTransformResource("META-INF/LICENSE.md")).isTrue() - assertThat(canTransformResource("LICENSE")).isTrue() - assertThat(canTransformResource("LICENSE.txt")).isTrue() - assertThat(canTransformResource("LICENSE.md")).isTrue() - assertThat(canTransformResource("something else")).isFalse() - } + fun defaultIncludes() = with(transformer) { + assertThat(canTransformResource("META-INF/LICENSE")).isTrue() + assertThat(canTransformResource("META-INF/LICENSE.txt")).isTrue() + assertThat(canTransformResource("META-INF/LICENSE.md")).isTrue() + assertThat(canTransformResource("LICENSE")).isTrue() + assertThat(canTransformResource("LICENSE.txt")).isTrue() + assertThat(canTransformResource("LICENSE.md")).isTrue() + assertThat(canTransformResource("something else")).isFalse() } @Test - fun customIncludes() { - with(transformer) { - include("META-INF/FOO") - exclude("META-INF/LICENSE*") - exclude("LICENSE*") - assertThat(canTransformResource("META-INF/FOO")).isTrue() - assertThat(canTransformResource("META-INF/LICENSE")).isFalse() - assertThat(canTransformResource("META-INF/LICENSE.txt")).isFalse() - assertThat(canTransformResource("META-INF/LICENSE.md")).isFalse() - assertThat(canTransformResource("LICENSE")).isFalse() - assertThat(canTransformResource("LICENSE.txt")).isFalse() - assertThat(canTransformResource("LICENSE.md")).isFalse() - assertThat(canTransformResource("something else")).isFalse() - } + fun customIncludes() = with(transformer) { + include("META-INF/FOO") + exclude("META-INF/LICENSE*") + exclude("LICENSE*") + assertThat(canTransformResource("META-INF/FOO")).isTrue() + assertThat(canTransformResource("META-INF/LICENSE")).isFalse() + assertThat(canTransformResource("META-INF/LICENSE.txt")).isFalse() + assertThat(canTransformResource("META-INF/LICENSE.md")).isFalse() + assertThat(canTransformResource("LICENSE")).isFalse() + assertThat(canTransformResource("LICENSE.txt")).isFalse() + assertThat(canTransformResource("LICENSE.md")).isFalse() + assertThat(canTransformResource("something else")).isFalse() } @Test - fun deduplicateLicenseTexts(@TempDir tempDir: Path) { - with(transformer) { - transformInternal("license one".toByteArray()) - transformInternal("\r\nlicense one\r\n".toByteArray()) - transformInternal("\nlicense one\n".toByteArray()) - transformInternal(" license two".toByteArray()) - transformInternal("\r\n\n\r\n\n license two".toByteArray()) - transformInternal(" license two\r\n\n\r\n\n".toByteArray()) - transformInternal("license three".toByteArray()) + fun deduplicateLicenseTexts(@TempDir tempDir: Path) = with(transformer) { + transformInternal("license one".toByteArray()) + transformInternal("\r\nlicense one\r\n".toByteArray()) + transformInternal("\nlicense one\n".toByteArray()) + transformInternal(" license two".toByteArray()) + transformInternal("\r\n\n\r\n\n license two".toByteArray()) + transformInternal(" license two\r\n\n\r\n\n".toByteArray()) + transformInternal("license three".toByteArray()) - val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() - artifactLicenseFile.writeText("artifact license file content") - artifactLicense.set(artifactLicenseFile) + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) - assertThat(elements).containsExactlyInAnyOrder("license one", " license two", "license three") + assertThat(elements).containsExactlyInAnyOrder("license one", " license two", "license three") - assertThat(buildLicense()).isEqualTo( - """ - SPDX-License-Identifier: Apache-2.0 - artifact license file content + assertThat(buildLicense()).isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license file content - ------------------------------------------------------------------------------------------------------------------------ + ------------------------------------------------------------------------------------------------------------------------ - This artifact includes dependencies with the following licenses: - ---------------------------------------------------------------- + This artifact includes dependencies with the following licenses: + ---------------------------------------------------------------- - license one + license one - ------------------------------------------------------------------------------------------------------------------------ + ------------------------------------------------------------------------------------------------------------------------ - license two + license two - ------------------------------------------------------------------------------------------------------------------------ + ------------------------------------------------------------------------------------------------------------------------ - license three - """.trimIndent(), - ) - } + license three + """.trimIndent(), + ) } @Test - fun singleAdditionalLicense(@TempDir tempDir: Path) { - with(transformer) { - transformInternal("license one".toByteArray()) + fun singleAdditionalLicense(@TempDir tempDir: Path) = with(transformer) { + transformInternal("license one".toByteArray()) - val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() - artifactLicenseFile.writeText("artifact license file content") - artifactLicense.set(artifactLicenseFile) + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) - assertThat(elements).containsExactlyInAnyOrder("license one") + assertThat(elements).containsExactlyInAnyOrder("license one") - assertThat(buildLicense()).isEqualTo( - """ - SPDX-License-Identifier: Apache-2.0 - artifact license file content + assertThat(buildLicense()).isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license file content - ------------------------------------------------------------------------------------------------------------------------ + ------------------------------------------------------------------------------------------------------------------------ - This artifact includes dependencies with the following licenses: - ---------------------------------------------------------------- + This artifact includes dependencies with the following licenses: + ---------------------------------------------------------------- - license one - """.trimIndent(), - ) - } + license one + """.trimIndent(), + ) } @Test - fun noAdditionalLicenses(@TempDir tempDir: Path) { - with(transformer) { - val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() - artifactLicenseFile.writeText("artifact license file content") - artifactLicense.set(artifactLicenseFile) - - assertThat(elements).isEmpty() - - assertThat(buildLicense()).isEqualTo( - """ - SPDX-License-Identifier: Apache-2.0 - artifact license file content - """.trimIndent(), - ) - } + fun noAdditionalLicenses(@TempDir tempDir: Path) = with(transformer) { + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) + + assertThat(elements).isEmpty() + + assertThat(buildLicense()).isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license file content + """.trimIndent(), + ) } @Test - fun noSpdxId(@TempDir tempDir: Path) { - with(transformer) { - artifactLicenseSpdxId.unsetConvention() + fun noSpdxId(@TempDir tempDir: Path) = with(transformer) { + artifactLicenseSpdxId.unsetConvention() - val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() - artifactLicenseFile.writeText("artifact license file content") - artifactLicense.set(artifactLicenseFile) + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) - assertThat(elements).isEmpty() + assertThat(elements).isEmpty() - assertThat(buildLicense()).isEqualTo( - "artifact license file content", - ) - } + assertThat(buildLicense()).isEqualTo( + "artifact license file content", + ) } } From 7c3dcee76fd01f2381d675ddc65a13bb714f12b7 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 25 Nov 2025 18:23:33 +0800 Subject: [PATCH 872/941] Deprecate ShadowCopyAction (#1876) --- docs/changes/README.md | 2 ++ .../jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 4 +++- .../github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 23ec001fd..e6926c024 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -22,6 +22,8 @@ - Bump min Gradle requirement to 9.0.0. ([#1801](https://github.com/GradleUp/shadow/pull/1801)) - Deprecate `PreserveFirstFoundResourceTransformer.resources`. ([#1855](https://github.com/GradleUp/shadow/pull/1855)) - Make the output of `PropertiesFileTransformer` reproducible. ([#1861](https://github.com/GradleUp/shadow/pull/1861)) +- Deprecate `ShadowCopyAction`. ([#1876](https://github.com/GradleUp/shadow/pull/1876)) + It should not be used as a public API. Will be made internal in a future release. ### Fixed diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index a5cdac30c..f78b19970 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -37,7 +37,9 @@ import org.objectweb.asm.commons.ClassRemapper /** * Modified from [org.gradle.api.internal.file.archive.ZipCopyAction.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java). */ -public open class ShadowCopyAction( +public open class ShadowCopyAction +@Deprecated("This should not be used as a public API. Will be made internal in a future release.") +constructor( private val zipFile: File, private val zosProvider: (File) -> ZipOutputStream, private val transformers: Set, diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 685df526c..200414f0e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -425,6 +425,7 @@ public abstract class ShadowJar : Jar() { } else { emptySet() } + @Suppress("DEPRECATION") return ShadowCopyAction( zipFile = archiveFile.get().asFile, zosProvider = zosProvider, From 2aec97dd8cf7e2198ccf140ebe65b7f268744cf1 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Wed, 26 Nov 2025 15:53:39 +0800 Subject: [PATCH 873/941] Support disabling Kotlin module metadata remapping (#1875) * Add `enableKotlinModuleRelocation` * Update changelog * Update tests * Rename `enableKotlinModuleRelocation` to `enableKotlinModuleRemapping` * Refine changelog --- api/shadow.api | 3 +- docs/changes/README.md | 7 +++++ docs/getting-started/README.md | 24 +++++++------- .../gradle/plugins/shadow/JavaPluginsTest.kt | 2 ++ .../gradle/plugins/shadow/RelocationTest.kt | 31 ++++++++++++++----- .../plugins/shadow/tasks/ShadowCopyAction.kt | 3 +- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 15 ++++++++- .../plugins/shadow/ShadowPropertiesTest.kt | 1 + 8 files changed, 64 insertions(+), 22 deletions(-) diff --git a/api/shadow.api b/api/shadow.api index cc21666cf..218f8cb7e 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -203,7 +203,7 @@ public abstract interface class com/github/jengelman/gradle/plugins/shadow/tasks public class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction : org/gradle/api/internal/file/copy/CopyAction { public static final field Companion Lcom/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction$Companion; - public fun (Ljava/io/File;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZZLjava/lang/String;)V + public fun (Ljava/io/File;Lkotlin/jvm/functions/Function1;Ljava/util/Set;Ljava/util/Set;Ljava/util/Set;ZZZLjava/lang/String;)V public fun execute (Lorg/gradle/api/internal/file/copy/CopyActionProcessingStream;)Lorg/gradle/api/tasks/WorkResult; } @@ -228,6 +228,7 @@ public abstract class com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar public fun getDependencyFilter ()Lorg/gradle/api/provider/Property; public fun getDuplicatesStrategy ()Lorg/gradle/api/file/DuplicatesStrategy; public fun getEnableAutoRelocation ()Lorg/gradle/api/provider/Property; + public fun getEnableKotlinModuleRemapping ()Lorg/gradle/api/provider/Property; public fun getExcludes ()Ljava/util/Set; public fun getFailOnDuplicateEntries ()Lorg/gradle/api/provider/Property; public fun getIncludedDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection; diff --git a/docs/changes/README.md b/docs/changes/README.md index e6926c024..65c431963 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -14,6 +14,13 @@ - Add new merge strategy `Fail` to `PropertiesFileTransformer`. ([#1856](https://github.com/GradleUp/shadow/pull/1856)) - Add `FindResourceInClasspath` task to help with debugging issues with merged duplicate resources. ([#1860](https://github.com/GradleUp/shadow/pull/1860)) - Add `MergeLicenseResourceTransformer`. ([#1858](https://github.com/GradleUp/shadow/pull/1858)) +- Support disabling Kotlin module metadata remapping. ([#1875](https://github.com/GradleUp/shadow/pull/1875)) + ```kotlin + tasks.shadowJar { + // Disable remapping of Kotlin module metadata (`.kotlin_module`) files. This is enabled by default. + enableKotlinModuleRemapping = false + } + ``` ### Changed diff --git a/docs/getting-started/README.md b/docs/getting-started/README.md index 287c32528..6d370c541 100644 --- a/docs/getting-started/README.md +++ b/docs/getting-started/README.md @@ -148,17 +148,19 @@ build script. Passing property values on the command line is particularly helpfu Here are the options that can be passed to the `shadowJar`: ``` ---add-multi-release-attribute Adds the multi-release attribute to the manifest if any dependencies contain it. ---no-add-multi-release-attribute Disables option --add-multi-release-attribute. ---enable-auto-relocation Enables auto relocation of packages in the dependencies. ---no-enable-auto-relocation Disables option --enable-auto-relocation. ---fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate. ---no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries. ---main-class Main class attribute to add to manifest. ---minimize-jar Minimizes the jar by removing unused classes. ---no-minimize-jar Disables option --minimize-jar. ---relocation-prefix Prefix used for auto relocation of packages in the dependencies. ---rerun Causes the task to be re-run even if up-to-date. +--add-multi-release-attribute Adds the multi-release attribute to the manifest if any dependencies contain it. +--no-add-multi-release-attribute Disables option --add-multi-release-attribute. +--enable-auto-relocation Enables auto relocation of packages in the dependencies. +--no-enable-auto-relocation Disables option --enable-auto-relocation. +--enable-kotlin-module-remapping Enables remapping of Kotlin module metadata files. +--no-enable-kotlin-module-remapping Disables option --enable-kotlin-module-remapping. +--fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate. +--no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries. +--main-class Main class attribute to add to manifest. +--minimize-jar Minimizes the jar by removing unused classes. +--no-minimize-jar Disables option --minimize-jar. +--relocation-prefix Prefix used for auto relocation of packages in the dependencies. +--rerun Causes the task to be re-run even if up-to-date. ``` Also, you can view more information about the [`ShadowJar`][ShadowJar] task by running the following command: diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index e307c2541..f1e3d4f2d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -100,6 +100,8 @@ class JavaPluginsTest : BasePluginTest() { "--no-add-multi-release-attribute Disables option --add-multi-release-attribute.", "--enable-auto-relocation Enables auto relocation of packages in the dependencies.", "--no-enable-auto-relocation Disables option --enable-auto-relocation.", + "--enable-kotlin-module-remapping Enables remapping of Kotlin module metadata files.", + "--no-enable-kotlin-module-remapping Disables option --enable-kotlin-module-remapping.", "--fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate.", "--no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries", "--main-class Main class attribute to add to manifest.", diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index d3aef137c..9a1244388 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -603,12 +603,14 @@ class RelocationTest : BasePluginTest() { @Issue( "https://github.com/GradleUp/shadow/issues/843", ) - @Test @OptIn(UnstableMetadataApi::class) - fun relocateKotlinModuleFiles() { + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun relocateKotlinModuleFiles(enableKotlinModuleRemapping: Boolean) { val originalModuleFilePath = "META-INF/kotlin-stdlib.kotlin_module" + val originalModuleFileBytes = requireResourceAsPath(originalModuleFilePath).readBytes() val stdlibJar = buildJar("stdlib.jar") { - insert(originalModuleFilePath, requireResourceAsPath(originalModuleFilePath).readBytes()) + insert(originalModuleFilePath, originalModuleFileBytes) } projectScript.appendText( """ @@ -617,6 +619,7 @@ class RelocationTest : BasePluginTest() { } $shadowJarTask { relocate('kotlin', 'my.kotlin') + enableKotlinModuleRemapping = $enableKotlinModuleRemapping } """.trimIndent(), ) @@ -624,11 +627,23 @@ class RelocationTest : BasePluginTest() { runWithSuccess(shadowJarPath) val relocatedModuleFilePath = "META-INF/kotlin-stdlib.shadow.kotlin_module" - assertThat(outputShadowedJar).useAll { - containsOnly( - relocatedModuleFilePath, - *manifestEntries, - ) + + if (enableKotlinModuleRemapping) { + assertThat(outputShadowedJar).useAll { + containsOnly( + relocatedModuleFilePath, + *manifestEntries, + ) + } + } else { + assertThat(outputShadowedJar).useAll { + containsOnly( + originalModuleFilePath, + *manifestEntries, + ) + } + assertThat(outputShadowedJar.use { it.getBytes(originalModuleFilePath) }).isEqualTo(originalModuleFileBytes) + return } val originalModule = KotlinModuleMetadata.read(requireResourceAsStream(originalModuleFilePath).readBytes()) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index f78b19970..428c77fd2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -45,6 +45,7 @@ constructor( private val transformers: Set, private val relocators: Set, private val unusedClasses: Set, + private val enableKotlinModuleRemapping: Boolean, private val preserveFileTimestamps: Boolean, private val failOnDuplicateEntries: Boolean, private val encoding: String?, @@ -175,7 +176,7 @@ constructor( fileDetails.remapClass() } } - path.endsWith(".kotlin_module") -> { + enableKotlinModuleRemapping && path.endsWith(".kotlin_module") -> { if (relocators.isEmpty()) { fileDetails.writeToZip(path) } else { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 200414f0e..8171b817c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -1,7 +1,6 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin -import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.DefaultInheritManifest import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter @@ -160,6 +159,19 @@ public abstract class ShadowJar : Jar() { @get:Option(option = "enable-auto-relocation", description = "Enables auto relocation of packages in the dependencies.") public open val enableAutoRelocation: Property = objectFactory.property(false) + /** + * Enables remapping of Kotlin module metadata (`.kotlin_module`) files. + * + * If you enable this option, the Kotlin module metadata file paths and their contents will be relocated if they are + * matched by any of the configured [relocators]. Someone may want to disable this feature and write their own + * [ResourceTransformer]s to handle Kotlin module metadata files in a custom way. + * + * Defaults to `true`. + */ + @get:Input + @get:Option(option = "enable-kotlin-module-remapping", description = "Enables remapping of Kotlin module metadata files.") + public open val enableKotlinModuleRemapping: Property = objectFactory.property(true) + /** * Prefix used for auto relocation of packages in the dependencies. * @@ -432,6 +444,7 @@ public abstract class ShadowJar : Jar() { transformers = transformers.get(), relocators = relocators.get() + packageRelocators, unusedClasses = unusedClasses, + enableKotlinModuleRemapping = enableKotlinModuleRemapping.get(), preserveFileTimestamps = isPreserveFileTimestamps, failOnDuplicateEntries = failOnDuplicateEntries.get(), metadataCharset, diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index 6a886394b..2fdaadf7e 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -147,6 +147,7 @@ class ShadowPropertiesTest { with(shadowJarTask) { assertThat(addMultiReleaseAttribute.get()).isTrue() assertThat(enableAutoRelocation.get()).isFalse() + assertThat(enableKotlinModuleRemapping.get()).isTrue() assertThat(failOnDuplicateEntries.get()).isFalse() assertThat(minimizeJar.get()).isFalse() assertThat(mainClass.orNull).isNull() From 6e20142b0beba643bea94a14f0ec1746d5c41786 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sat, 29 Nov 2025 15:27:29 +0800 Subject: [PATCH 874/941] Update the comment for associateWith --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index afb1d848f..45cc3008b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -224,7 +224,7 @@ gradlePlugin { kotlin.target.compilations { val main by getting getByName("functionalTest") { - // TODO: https://youtrack.jetbrains.com/issue/KTIJ-7662 + // Share main's output with functionalTest. associateWith(main) } } From 0e1538b1a67eb01633ca2661accb1c0366bd99be Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 29 Nov 2025 20:46:17 +0800 Subject: [PATCH 875/941] Read optional properties from ExtraPropertiesExtension (#1877) --- .../gradle/plugins/shadow/internal/GradleCompat.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index f06880ab4..03c281209 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -12,6 +12,7 @@ import org.gradle.api.component.ConfigurationVariantDetails import org.gradle.api.distribution.DistributionContainer import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtraPropertiesExtension import org.gradle.api.plugins.JavaApplication import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPluginExtension @@ -45,9 +46,12 @@ internal inline val Project.javaPluginExtension: JavaPluginExtension internal inline val Project.javaToolchainService: JavaToolchainService get() = extensions.getByType(JavaToolchainService::class.java) -@Suppress("GradleProjectIsolation") // TODO: https://github.com/gradle/gradle/issues/23572 -internal fun Project.findOptionalProperty(propertyName: String): String? = providers.gradleProperty(propertyName).orNull - ?: findProperty(propertyName)?.toString() +// ExtraPropertiesExtension is IP safe and contains properties from both the root `gradle.properties` and the +// subproject's `gradle.properties`. See https://github.com/gradle/gradle/issues/29600#issuecomment-3580868326. +internal fun Project.findOptionalProperty(propertyName: String): String? { + val extras = checkNotNull(extensions.findByType(ExtraPropertiesExtension::class.java)) + return if (extras.has(propertyName)) extras.get(propertyName)?.toString() else null +} internal fun Project.addBuildScanCustomValues() { val develocity = extensions.findByType(DevelocityConfiguration::class.java) ?: return From 1994b96b233bce7a6dd392e3524edb623859a8b0 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 30 Nov 2025 13:03:31 +0800 Subject: [PATCH 876/941] Use checkNotNull --- build.gradle.kts | 2 +- .../github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt | 2 +- .../github/jengelman/gradle/plugins/shadow/PublishingTest.kt | 2 +- .../github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 45cc3008b..a558b798c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -181,7 +181,7 @@ testing.suites { } withType().configureEach { - useJUnitJupiter(libs.junit.bom.map { requireNotNull(it.version) }) + useJUnitJupiter(libs.junit.bom.map { checkNotNull(it.version) }) dependencies { implementation(testKit.output) implementation(libs.assertk) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index f1e3d4f2d..767325988 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -54,7 +54,7 @@ class JavaPluginsTest : BasePluginTest() { fun makeAssembleDependOnShadowJarEvenIfAddedLater() { val kFunction = ShadowJar.Companion::class.declaredFunctions .single { it.name == "registerShadowJarCommon" } - val jvmName = requireNotNull(kFunction.javaMethod).name + val jvmName = checkNotNull(kFunction.javaMethod).name projectScript.writeText( """ diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 8178d666a..181bd3711 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -715,7 +715,7 @@ class PublishingTest : BasePluginTest() { fun MavenXpp3Reader.read(path: Path): Model = path.inputStream().use { read(it) } - fun JsonAdapter.fromJson(path: Path): T = requireNotNull(fromJson(path.readText())) + fun JsonAdapter.fromJson(path: Path): T = checkNotNull(fromJson(path.readText())) val Path.entries: List get() = listDirectoryEntries().map { it.name } } diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt index a213bc939..41b41878f 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt @@ -38,7 +38,7 @@ fun ZipFile.getContent(entryName: String): String { } fun ZipFile.getStream(entryName: String): InputStream { - val entry = requireNotNull(getEntry(entryName)) { "Entry $entryName not found in all entries: ${entries().toList()}" } + val entry = checkNotNull(getEntry(entryName)) { "Entry $entryName not found in all entries: ${entries().toList()}" } return getInputStream(entry) } From 30f314c1ba5f60d120c9ce90a450fee0268bfa38 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 04:12:04 +0800 Subject: [PATCH 877/941] Update ffurrer2/extract-release-notes action to v3 (#1878) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a42245e8b..48ddf7557 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_KEY }} ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_KEY_PASSWORD }} - name: Extract release notes - uses: ffurrer2/extract-release-notes@v2 + uses: ffurrer2/extract-release-notes@v3 with: changelog_file: docs/changes/README.md release_notes_file: RELEASE_NOTES.md From b3fbe09eb67ed3e0413c6e344170fabf3fe1a8eb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 1 Dec 2025 12:21:26 +0800 Subject: [PATCH 878/941] Add edit buttons to doc pages (#1880) --- mkdocs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mkdocs.yml b/mkdocs.yml index d35a3f1d1..48ab65f44 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -8,6 +8,7 @@ repo_url: https://github.com/GradleUp/shadow site_description: "Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries" site_author: GradleUp developers remote_branch: gh-pages +edit_uri: edit/main/docs theme: name: 'material' @@ -32,6 +33,7 @@ theme: features: - content.code.copy - content.code.select + - content.action.edit markdown_extensions: - smarty From b6f1f59174b8af63c535eed2864ea4da9415ffcb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 23:35:17 +0800 Subject: [PATCH 879/941] Update kotlin monorepo to v2.3.0-RC2 (#1881) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5225849c3..66d678c43 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] jdkRelease = "17" minGradle = "9.0.0" -kotlin = "2.3.0-RC" +kotlin = "2.3.0-RC2" moshi = "1.15.2" pluginPublish = "2.0.0" From 99e399158604572770da456e4068316733e2f652 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Fri, 5 Dec 2025 07:55:08 +0100 Subject: [PATCH 880/941] Add DeduplicatingResourceTransformer to deduplicate on path and content (#1859) Adds a new `DeduplicatingResourceTransformer` that works different than `PreserveFirstFoundResourceTransformer`. `PreserveFirstFoundResourceTransformer` is to preserve the first resource that matches the configured paths and ignore all other ones. `DeduplicatingResourceTransformer` preserves resources by path _and_ identical content and fails for all not explicitly allowed (excluded) resources with different content. It works intentionally against all resources. The new one is intended to guard a couple of unexpected situations: * A (transitive) dependency brings a non-relocated version of a dependency that is also included elsewhere but with a different version. This could normally lead to unexpected exceptions during runtime. * Unintended inclusion or removal or legally important license information, see also `MergeLicenseResourceTransformer`. * Unintended removal or (false) inclusion of shaded dependency information via `META-INF/x/y/pom.xml`/`.properties` files, which can be important for dependency/license analyzation tools. Adding the functionality of `DeduplicatingResourceTransformer` to `PreserveFirstFoundResourceTransformer` became a bit too difficult without breaking the existing behavior of the latter. --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Goooler --- api/shadow.api | 9 ++ build.gradle.kts | 1 + docs/changes/README.md | 1 + gradle/libs.versions.toml | 1 + .../shadow/transformers/TransformersTest.kt | 66 +++++++++ .../DeduplicatingResourceTransformer.kt | 129 ++++++++++++++++++ .../PreserveFirstFoundResourceTransformer.kt | 7 + .../DeduplicatingResourceTransformerTest.kt | 109 +++++++++++++++ .../gradle/plugins/shadow/testkit/JarPath.kt | 39 ++++++ 9 files changed, 362 insertions(+) create mode 100644 src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt create mode 100644 src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformerTest.kt diff --git a/api/shadow.api b/api/shadow.api index 218f8cb7e..0d90a6c24 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -329,6 +329,15 @@ public class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsX public final class com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer$Companion { } +public class com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer { + public fun (Lorg/gradle/api/model/ObjectFactory;)V + public fun (Lorg/gradle/api/model/ObjectFactory;Lorg/gradle/api/tasks/util/PatternSet;)V + public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z + public final fun getObjectFactory ()Lorg/gradle/api/model/ObjectFactory; + public fun hasTransformedResource ()Z + public fun modifyOutputStream (Lorg/apache/tools/zip/ZipOutputStream;Z)V +} + public class com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer : com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer { public fun (Lorg/gradle/api/model/ObjectFactory;)V public fun canTransformResource (Lorg/gradle/api/file/FileTreeElement;)Z diff --git a/build.gradle.kts b/build.gradle.kts index a558b798c..151dc231e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -119,6 +119,7 @@ dependencies { compileOnly(libs.kotlin.gradlePlugin) compileOnly(libs.kotlin.reflect) api(libs.apache.ant) // Types from Ant are exposed in the public API. + implementation(libs.apache.commonsCodec) implementation(libs.apache.commonsIo) implementation(libs.apache.log4j) implementation(libs.asm) diff --git a/docs/changes/README.md b/docs/changes/README.md index 65c431963..098586a10 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -21,6 +21,7 @@ enableKotlinModuleRemapping = false } ``` +- Add `DeduplicatingResourceTransformer` to deduplicate on path _and_ content. ([#1859](https://github.com/GradleUp/shadow/pull/1859)) ### Changed diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 66d678c43..f5e69498a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,6 +7,7 @@ pluginPublish = "2.0.0" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" +apache-commonsCodec = "commons-codec:commons-codec:1.20.0" apache-commonsIo = "commons-io:commons-io:2.21.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.2" apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.11" diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 9da1f572b..b36d8ecb1 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -2,13 +2,17 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import assertk.all import assertk.assertThat +import assertk.assertions.contains +import assertk.assertions.containsExactlyInAnyOrder import assertk.assertions.isEqualTo import assertk.assertions.isNotEqualTo import assertk.assertions.isNotNull import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.testkit.containsAtLeast +import com.github.jengelman.gradle.plugins.shadow.testkit.containsExactlyInAnyOrder import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly import com.github.jengelman.gradle.plugins.shadow.testkit.getContent +import com.github.jengelman.gradle.plugins.shadow.testkit.getContents import com.github.jengelman.gradle.plugins.shadow.testkit.getStream import com.github.jengelman.gradle.plugins.shadow.testkit.invariantEolString import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath @@ -20,11 +24,73 @@ import kotlin.io.path.readText import kotlin.io.path.writeText import kotlin.reflect.KClass import org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor.PLUGIN_CACHE_FILE +import org.gradle.testkit.runner.TaskOutcome.FAILED import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource class TransformersTest : BaseTransformerTest() { + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun deduplicatingResourceTransformer(excludeAll: Boolean) { + val one = buildJarOne { + insert("multiple-contents", "content") + insert("single-source", "content") + insert("same-content-twice", "content") + insert("differing-content-2", "content") + } + val two = buildJarTwo { + insert("multiple-contents", "content-is-different") + insert("same-content-twice", "content") + insert("differing-content-2", "content-is-different") + } + + projectScript.appendText( + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = """ + exclude('multiple-contents') + ${if (excludeAll) "exclude('differing-content-2')" else ""} + """.trimIndent(), + ), + ) + + if (excludeAll) { + runWithSuccess(shadowJarPath) + assertThat(outputShadowedJar).useAll { + containsExactlyInAnyOrder( + // twice: + "multiple-contents", + "multiple-contents", + "single-source", + "same-content-twice", + // twice: + "differing-content-2", + "differing-content-2", + "META-INF/", + "META-INF/MANIFEST.MF", + ) + getContents("multiple-contents").containsExactlyInAnyOrder("content", "content-is-different") + getContent("single-source").isEqualTo("content") + getContent("same-content-twice").isEqualTo("content") + getContents("differing-content-2").containsExactlyInAnyOrder("content", "content-is-different") + } + } else { + val buildResult = runWithFailure(shadowJarPath) + assertThat(buildResult).taskOutcomeEquals(shadowJarPath, FAILED) + assertThat(buildResult.output).contains( + // Keep this list approach for Unix/Windows test compatibility. + "Execution failed for task ':shadowJar'.", + "> Found 1 path duplicate(s) with different content in the shadowed JAR:", + " * differing-content-2", + "differing-content-2 (SHA256: ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73)", + "differing-content-2 (SHA256: aa845861bbd4578700e10487d85b25ead8723ee98fbf143df7b7e0bf1cb3385d)", + ) + } + } + @Test fun manifestRetained() { writeClass() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt new file mode 100644 index 000000000..3c5498a84 --- /dev/null +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt @@ -0,0 +1,129 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import com.github.jengelman.gradle.plugins.shadow.tasks.FindResourceInClasspath +import java.io.File +import javax.inject.Inject +import org.apache.commons.codec.digest.DigestUtils +import org.apache.tools.zip.ZipOutputStream +import org.gradle.api.GradleException +import org.gradle.api.file.FileTreeElement +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.util.PatternSet + +/** + * Transformer to include files with identical content only once in the shadowed JAR. + * + * Multiple files with the same path but different content lead to an error. + * + * Some scenarios for duplicate resources in a shadow jar: + * + * - Duplicate `.class` files + * Having duplicate `.class` files with different content is a situation indicating that the resulting jar is + * built with _incompatible_ classes, likely leading to issues during runtime. + * This situation can happen when one dependency is (also) included in an uber jar. + * + * - Duplicate `META-INF///pom.properties`/`xml` files. + * Some dependencies contain shaded variants of other dependencies. + * Tools that inspect jar files to extract the included dependencies, for example, for license auditing + * use cases or tools that collect information of all included dependencies, may rely on these files. + * Hence, it is desirable to retain the duplicate resource `pom.properties`/`xml` resources. + * + * [DeduplicatingResourceTransformer] checks all entries in the resulting jar. + * It is generally not recommended to use any of the [include] configuration functions. + * + * There are reasons to retain duplicate resources with different contents in the resulting jar. + * This can be achieved with the [exclude] configuration functions. + * + * To exclude a path or pattern from being deduplicated, for example, legit + * `META-INF///pom.properties`/`xml`, configure the transformer with an exclusion + * like the following: + * + * ```kotlin + * tasks.shadowJar { + * transform(DeduplicatingResourceTransformer::class.java) { + * // Keep pom.* files from different Guava versions in the jar. + * exclude("META-INF/maven/com.google.guava/guava/pom.*") + * // Duplicates with different content for all other resource paths will raise an error. + * } + * } + * ``` + * + * *Tip*: the [FindResourceInClasspath] convenience task can be used to find resources in a Gradle + * classpath/configuration. + * + * *Warning* Do **not** combine [PreserveFirstFoundResourceTransformer] with this transformer, + * as they handle duplicates differently and combining them would lead to redundant or unexpected behavior. + */ +@CacheableTransformer +public open class DeduplicatingResourceTransformer( + final override val objectFactory: ObjectFactory, + patternSet: PatternSet, +) : PatternFilterableResourceTransformer(patternSet) { + @get:Internal + internal val sources: MutableMap = mutableMapOf() + + @Inject + public constructor(objectFactory: ObjectFactory) : this(objectFactory, PatternSet()) + + override fun canTransformResource(element: FileTreeElement): Boolean { + val file = element.file + val hash = file.sha256Hex() + + val pathInfos = sources.computeIfAbsent(element.path) { + PathInfos(patternSpec.isSatisfiedBy(element)) + } + val retainInOutput = pathInfos.addFile(hash, file) + + return !retainInOutput + } + + override fun hasTransformedResource(): Boolean = true + + override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { + val duplicatePaths = duplicateContentViolations() + + if (duplicatePaths.isNotEmpty()) { + val message = buildString { + append("Found ${duplicatePaths.size} path duplicate(s) with different content in the shadowed JAR:\n") + duplicatePaths.forEach { (path, infos) -> + append(" * $path\n") + infos.filesPerHash.forEach { (hash, files) -> + files.forEach { file -> + append(" * ${file.path} (SHA256: $hash)\n") + } + } + } + } + throw GradleException(message) + } + } + + internal fun duplicateContentViolations(): Map = sources.filter { (_, pathInfos) -> + pathInfos.failOnDuplicateContent && pathInfos.uniqueContentCount() > 1 + } + + internal data class PathInfos(val failOnDuplicateContent: Boolean) { + val filesPerHash: MutableMap> = mutableMapOf() + + fun uniqueContentCount() = filesPerHash.size + + fun addFile(hash: String, file: File): Boolean { + val new = hash !in filesPerHash + filesPerHash.getOrPut(hash) { mutableListOf() }.add(file) + return new + } + } + + internal companion object { + fun File.sha256Hex(): String { + try { + return inputStream().use { + DigestUtils.sha256Hex(it) + } + } catch (e: Exception) { + throw RuntimeException("Failed to read data or calculate hash for $this", e) + } + } + } +} diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt index e0aaaa825..a8b6527f8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt @@ -19,6 +19,13 @@ import org.gradle.api.tasks.util.PatternSet * want to ensure that only the first found resource is included in the final JAR. If there are multiple resources with * the same path in a project and its dependencies, the first one found should be the project's. * + * This transformer deduplicates included resources based on the path name. + * See [DeduplicatingResourceTransformer] for a transformer that deduplicates based on the paths and contents of + * the resources. + * + * *Warning* Do **not** combine [DeduplicatingResourceTransformer] with this transformer, + * as they handle duplicates differently and combining them would lead to redundant or unexpected behavior. + * * @see [DuplicatesStrategy] * @see [ShadowJar.getDuplicatesStrategy] */ diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformerTest.kt new file mode 100644 index 000000000..eba93f87b --- /dev/null +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformerTest.kt @@ -0,0 +1,109 @@ +package com.github.jengelman.gradle.plugins.shadow.transformers + +import assertk.assertThat +import assertk.assertions.containsExactlyInAnyOrder +import assertk.assertions.containsOnly +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isTrue +import com.github.jengelman.gradle.plugins.shadow.transformers.DeduplicatingResourceTransformer.Companion.sha256Hex +import java.io.File +import java.nio.file.Path +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.io.TempDir +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.ValueSource + +class DeduplicatingResourceTransformerTest : BaseTransformerTest() { + + @TempDir + lateinit var tempDir: Path + + private lateinit var file1: File + private lateinit var file2: File + private lateinit var file3: File + + private var hash1 = "" + private var hash3 = "" + + @BeforeEach + fun setupFiles() { + val content1 = "content1" + val content2 = "content2" + + file1 = tempDir.resolve("file1").toFile().apply { + writeText(content1) + } + file2 = tempDir.resolve("file2").toFile().apply { + writeText(content1) + } + file3 = tempDir.resolve("file3").toFile().apply { + writeText(content2) + } + + hash1 = file1.sha256Hex() + hash3 = file3.sha256Hex() + } + + @ParameterizedTest + @ValueSource(booleans = [false, true]) + fun duplicateContent(exclusionCheck: Boolean) = with(transformer) { + if (!exclusionCheck) { + exclude("multiple-contents") + } + + // new path, new file content --> retain resource + assertThat(canTransformResource("multiple-contents", file1)).isFalse() + // same path, same file content --> skip resource + assertThat(canTransformResource("multiple-contents", file2)).isTrue() + // same path, different file content --> retain resource (even if it's a duplicate) + assertThat(canTransformResource("multiple-contents", file3)).isFalse() + + assertThat(canTransformResource("single-source", file1)).isFalse() + + assertThat(canTransformResource("same-content-twice", file1)).isFalse() + assertThat(canTransformResource("same-content-twice", file2)).isTrue() + + assertThat(canTransformResource("differing-content-2", file1)).isFalse() + assertThat(canTransformResource("differing-content-2", file3)).isFalse() + + assertThat(sources.keys).containsExactlyInAnyOrder( + "multiple-contents", + "single-source", + "same-content-twice", + "differing-content-2", + ) + + val pathInfosMultipleContents = sources.getValue("multiple-contents") + assertThat(pathInfosMultipleContents.failOnDuplicateContent).isEqualTo(exclusionCheck) + assertThat(pathInfosMultipleContents.uniqueContentCount()).isEqualTo(2) + assertThat(pathInfosMultipleContents.filesPerHash).containsOnly( + hash1 to listOf(file1, file2), + hash3 to listOf(file3), + ) + + val pathInfosSingleSource = sources.getValue("single-source") + assertThat(pathInfosSingleSource.failOnDuplicateContent).isTrue() + assertThat(pathInfosSingleSource.uniqueContentCount()).isEqualTo(1) + assertThat(pathInfosSingleSource.filesPerHash).containsOnly(hash1 to listOf(file1)) + + val pathInfosSameContentTwice = sources.getValue("same-content-twice") + assertThat(pathInfosSameContentTwice.failOnDuplicateContent).isTrue() + assertThat(pathInfosSameContentTwice.uniqueContentCount()).isEqualTo(1) + assertThat(pathInfosSameContentTwice.filesPerHash).containsOnly(hash1 to listOf(file1, file2)) + + val pathInfosDifferingContent2 = sources.getValue("differing-content-2") + assertThat(pathInfosDifferingContent2.failOnDuplicateContent).isTrue() + assertThat(pathInfosDifferingContent2.uniqueContentCount()).isEqualTo(2) + assertThat(pathInfosDifferingContent2.filesPerHash).containsOnly(hash1 to listOf(file1), hash3 to listOf(file3)) + + if (exclusionCheck) { + assertThat(duplicateContentViolations()).containsOnly( + "multiple-contents" to pathInfosMultipleContents, + "differing-content-2" to pathInfosDifferingContent2, + ) + } else { + assertThat(duplicateContentViolations()).containsOnly("differing-content-2" to pathInfosDifferingContent2) + } + } +} diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt index 41b41878f..41426f009 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt @@ -2,12 +2,16 @@ package com.github.jengelman.gradle.plugins.shadow.testkit import assertk.Assert import assertk.assertions.containsAtLeast +import assertk.assertions.containsExactlyInAnyOrder import assertk.assertions.containsNone import assertk.assertions.containsOnly import java.io.InputStream import java.nio.file.Path import java.util.jar.JarFile +import java.util.jar.JarInputStream import java.util.zip.ZipFile +import java.util.zip.ZipInputStream +import kotlin.io.path.inputStream /** * A wrapper for [JarFile] that also implements [Path]. @@ -44,6 +48,25 @@ fun ZipFile.getStream(entryName: String): InputStream { fun Assert.getContent(entryName: String) = transform { it.getContent(entryName) } +/** + * Scans the jar file for all entries that match the specified [entryName]. + * Unlike [getContent] or [getStream], which return only one of the matching entries + * (which one is undefined), this function returns all matching entries. + */ +fun Assert.getContents(entryName: String) = transform { actual -> + JarInputStream(actual.path.inputStream()).use { jarInput -> + val contents = mutableListOf() + while (true) { + val entry = jarInput.nextEntry ?: break + if (entry.name == entryName) { + contents.add(jarInput.readAllBytes().toString(Charsets.UTF_8)) + } + jarInput.closeEntry() + } + contents + } +} + fun Assert.getMainAttr(name: String) = transform { it.getMainAttr(name) } /** @@ -64,6 +87,22 @@ fun Assert.containsNone(vararg entries: String) = toEntries().containsN */ fun Assert.containsOnly(vararg entries: String) = toEntries().containsOnly(*entries) +/** + * Ensures the JAR contains exactly the specified entries, including duplicates, in any order. + * Used alone, without [containsAtLeast] or [containsNone]. + */ +fun Assert.containsExactlyInAnyOrder(vararg entries: String) = transform { actual -> + ZipInputStream(actual.path.inputStream()).use { jarInput -> + val allEntries = mutableListOf() + while (true) { + val entry = jarInput.nextEntry ?: break + allEntries.add(entry.name) + jarInput.closeEntry() + } + allEntries + } +}.containsExactlyInAnyOrder(*entries) + private fun Assert.toEntries() = transform { actual -> actual.entries().toList().map { it.name } } From 47a1e7bb9ab0c2330de85b0a75d57340e158588c Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Fri, 5 Dec 2025 11:21:34 +0100 Subject: [PATCH 881/941] Enhance docs about reproducible builds (#1882) * Enhance docs about reproducible builds * Apply suggestions from code review * Update docs/configuration/reproducible-builds/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Zongle Wang Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../reproducible-builds/README.md | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/docs/configuration/reproducible-builds/README.md b/docs/configuration/reproducible-builds/README.md index 6b4b1b609..4af22701c 100644 --- a/docs/configuration/reproducible-builds/README.md +++ b/docs/configuration/reproducible-builds/README.md @@ -3,23 +3,52 @@ By default, JAR files generated by Gradle (with or without Shadow) for a single project with the same source code may not be identical to each other. Sometimes it's desirable to configure a project to consistently output a byte-for-byte identical JAR on every build. Gradle supports this with the following configuration, and Shadow will correctly respect -these settings too: +these settings too. + +Besides file timestamps and file order, this configuration also ensures that all files in the JAR are set to have the +same permissions, irrespective of the locally configured umask. + +More information about reproducible builds can be found at [reproducible-builds.org](https://reproducible-builds.org/). === "Kotlin" ```kotlin + import java.nio.file.Files + import java.nio.file.attribute.PosixFilePermission + tasks.withType().configureEach { isPreserveFileTimestamps = false isReproducibleFileOrder = true + + eachFile { + permissions { + val isExec = + Files.getPosixFilePermissions(file.toPath()).contains(PosixFilePermission.OWNER_EXECUTE) + unix(if (isExec) "755" else "644") + } + } + dirPermissions { unix("755") } } ``` === "Groovy" ```groovy + import java.nio.file.Files + import java.nio.file.attribute.PosixFilePermission + tasks.withType(AbstractArchiveTask).configureEach { preserveFileTimestamps = false reproducibleFileOrder = true + + eachFile { + permissions { + def isExec = + Files.getPosixFilePermissions(file.toPath()).contains(PosixFilePermission.OWNER_EXECUTE) + unix(isExec ? '755' : '644') + } + } + dirPermissions { unix('755') } } ``` From d2227b295a05ec0070a1c2945bacac5291889852 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 5 Dec 2025 18:31:40 +0800 Subject: [PATCH 882/941] Remove reproducible-builds link --- docs/configuration/reproducible-builds/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/configuration/reproducible-builds/README.md b/docs/configuration/reproducible-builds/README.md index 4af22701c..7e2dbd554 100644 --- a/docs/configuration/reproducible-builds/README.md +++ b/docs/configuration/reproducible-builds/README.md @@ -8,8 +8,6 @@ these settings too. Besides file timestamps and file order, this configuration also ensures that all files in the JAR are set to have the same permissions, irrespective of the locally configured umask. -More information about reproducible builds can be found at [reproducible-builds.org](https://reproducible-builds.org/). - === "Kotlin" ```kotlin From ba40578e4b2ef51d0d6d31740ae7c0645e8dfa32 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 5 Dec 2025 23:19:31 +0800 Subject: [PATCH 883/941] Fix changelog links --- docs/changes/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 098586a10..f01cb5396 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -14,6 +14,7 @@ - Add new merge strategy `Fail` to `PropertiesFileTransformer`. ([#1856](https://github.com/GradleUp/shadow/pull/1856)) - Add `FindResourceInClasspath` task to help with debugging issues with merged duplicate resources. ([#1860](https://github.com/GradleUp/shadow/pull/1860)) - Add `MergeLicenseResourceTransformer`. ([#1858](https://github.com/GradleUp/shadow/pull/1858)) +- Add `DeduplicatingResourceTransformer` to deduplicate on path _and_ content. ([#1859](https://github.com/GradleUp/shadow/pull/1859)) - Support disabling Kotlin module metadata remapping. ([#1875](https://github.com/GradleUp/shadow/pull/1875)) ```kotlin tasks.shadowJar { @@ -21,7 +22,6 @@ enableKotlinModuleRemapping = false } ``` -- Add `DeduplicatingResourceTransformer` to deduplicate on path _and_ content. ([#1859](https://github.com/GradleUp/shadow/pull/1859)) ### Changed @@ -37,13 +37,13 @@ - Fix Develocity integration when Isolated Projects enabled. ([#1836](https://github.com/GradleUp/shadow/pull/1836)) -## [9.2.2](https://github.com/GradleUp/shadow/compare/9.2.2) - 2025-09-26 +## [9.2.2](https://github.com/GradleUp/shadow/releases/tag/9.2.2) - 2025-09-26 ### Fixed - Fix the regression of registering `ShadowJar` tasks without `ShadowPlugin` applied. ([#1787](https://github.com/GradleUp/shadow/pull/1787)) -## [9.2.1](https://github.com/GradleUp/shadow/compare/9.2.1) - 2025-09-24 +## [9.2.1](https://github.com/GradleUp/shadow/releases/tag/9.2.1) - 2025-09-24 ### Added @@ -103,7 +103,7 @@ - Fix excluding dependencies whose versions contain `+`. ([#1597](https://github.com/GradleUp/shadow/pull/1597)) -## [9.1.0](https://github.com/GradleUp/shadow/compare/9.1.0) - 2025-08-29 +## [9.1.0](https://github.com/GradleUp/shadow/releases/tag/9.1.0) - 2025-08-29 ### Added From bfc351ef9b9c1a00011deaf24f5160c65fd8280c Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 5 Dec 2025 23:25:36 +0800 Subject: [PATCH 884/941] Prepare version 9.3.0 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index f01cb5396..cd3f25cd4 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.2.2...HEAD) - 2025-xx-xx +## [9.3.0](https://github.com/GradleUp/shadow/releases/tag/9.3.0) - 2025-12-05 ### Added diff --git a/gradle.properties b/gradle.properties index 82b7edf50..ca7049813 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.2.3-SNAPSHOT +VERSION_NAME=9.3.0 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 2399ab3815695b995274db5156e867a3489f1eab Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 5 Dec 2025 23:26:08 +0800 Subject: [PATCH 885/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index cd3f25cd4..307925608 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.0...HEAD) - 2025-xx-xx + + ## [9.3.0](https://github.com/GradleUp/shadow/releases/tag/9.3.0) - 2025-12-05 ### Added diff --git a/gradle.properties b/gradle.properties index ca7049813..caeded690 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.3.0 +VERSION_NAME=9.3.1-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From e689fc0570cf7f297614cf9ff4e8467773a0dc34 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Dec 2025 21:50:46 +0800 Subject: [PATCH 886/941] Update kotlin monorepo to v2.3.0-RC3 (#1883) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f5e69498a..769f265c3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] jdkRelease = "17" minGradle = "9.0.0" -kotlin = "2.3.0-RC2" +kotlin = "2.3.0-RC3" moshi = "1.15.2" pluginPublish = "2.0.0" From 4ad300411adeb485b8390020f337068c6de22ce1 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 10 Dec 2025 21:41:47 +0800 Subject: [PATCH 887/941] Revert "Remove reproducible-builds link" This reverts commit d2227b295a05ec0070a1c2945bacac5291889852. --- docs/configuration/reproducible-builds/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/configuration/reproducible-builds/README.md b/docs/configuration/reproducible-builds/README.md index 7e2dbd554..4af22701c 100644 --- a/docs/configuration/reproducible-builds/README.md +++ b/docs/configuration/reproducible-builds/README.md @@ -8,6 +8,8 @@ these settings too. Besides file timestamps and file order, this configuration also ensures that all files in the JAR are set to have the same permissions, irrespective of the locally configured umask. +More information about reproducible builds can be found at [reproducible-builds.org](https://reproducible-builds.org/). + === "Kotlin" ```kotlin From 942a5474702d0cc10469999a3482cecc0730b270 Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 10 Dec 2025 23:35:08 +0800 Subject: [PATCH 888/941] Reformat ShadowJar --- .editorconfig | 1 + .../gradle/plugins/shadow/tasks/ShadowJar.kt | 25 +++++++++++++++---- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/.editorconfig b/.editorconfig index ccbd273ec..379e533b4 100755 --- a/.editorconfig +++ b/.editorconfig @@ -11,6 +11,7 @@ trim_trailing_whitespace = true ij_kotlin_imports_layout = * ij_kotlin_allow_trailing_comma = true ij_kotlin_allow_trailing_comma_on_call_site = true +ij_kotlin_continuation_indent_size = 2 ij_kotlin_line_break_after_multiline_when_entry = false ij_kotlin_name_count_to_use_star_import = 2147483647 ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 8171b817c..91b7319a8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -156,7 +156,10 @@ public abstract class ShadowJar : Jar() { * @see relocationPrefix */ @get:Input - @get:Option(option = "enable-auto-relocation", description = "Enables auto relocation of packages in the dependencies.") + @get:Option( + option = "enable-auto-relocation", + description = "Enables auto relocation of packages in the dependencies.", + ) public open val enableAutoRelocation: Property = objectFactory.property(false) /** @@ -169,7 +172,10 @@ public abstract class ShadowJar : Jar() { * Defaults to `true`. */ @get:Input - @get:Option(option = "enable-kotlin-module-remapping", description = "Enables remapping of Kotlin module metadata files.") + @get:Option( + option = "enable-kotlin-module-remapping", + description = "Enables remapping of Kotlin module metadata files.", + ) public open val enableKotlinModuleRemapping: Property = objectFactory.property(true) /** @@ -180,7 +186,10 @@ public abstract class ShadowJar : Jar() { * @see enableAutoRelocation */ @get:Input - @get:Option(option = "relocation-prefix", description = "Prefix used for auto relocation of packages in the dependencies.") + @get:Option( + option = "relocation-prefix", + description = "Prefix used for auto relocation of packages in the dependencies.", + ) public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) /** @@ -208,7 +217,10 @@ public abstract class ShadowJar : Jar() { * Defaults to `false`. */ @get:Input - @get:Option(option = "fail-on-duplicate-entries", description = "Fails build if the ZIP entries in the shadowed JAR are duplicate.") + @get:Option( + option = "fail-on-duplicate-entries", + description = "Fails build if the ZIP entries in the shadowed JAR are duplicate.", + ) public open val failOnDuplicateEntries: Property = objectFactory.property(false) /** @@ -218,7 +230,10 @@ public abstract class ShadowJar : Jar() { * Defaults to `true`. */ @get:Input - @get:Option(option = "add-multi-release-attribute", description = "Adds the multi-release attribute to the manifest if any dependencies contain it.") + @get:Option( + option = "add-multi-release-attribute", + description = "Adds the multi-release attribute to the manifest if any dependencies contain it.", + ) public open val addMultiReleaseAttribute: Property = objectFactory.property(true) @Suppress("DEPRECATION") // TODO: replace the usage of deprecated InheritManifest. From 35f92f35e0cdf759d311756da3a3d7db56bfa2e3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 09:04:44 +0800 Subject: [PATCH 889/941] Update Develocity to v4.3 (#1886) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 769f265c3..51e2eb150 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ 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:1.0.0" -develocity = "com.gradle:develocity-gradle-plugin:4.2.2" +develocity = "com.gradle:develocity-gradle-plugin:4.3" kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 78d76bbfd..af496d121 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -13,7 +13,7 @@ pluginManagement { } plugins { - id("com.gradle.develocity") version "4.2.2" + id("com.gradle.develocity") version "4.3" } develocity { From f36ff70ad82b288703b00643a349909282e5e3fb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Dec 2025 22:22:45 +0800 Subject: [PATCH 890/941] Update plugin android-lint to v8.13.2 (#1887) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 51e2eb150..b087af082 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,7 +37,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.13.1" +android-lint = "com.android.lint:8.13.2" jetbrains-dokka = "org.jetbrains.dokka:2.1.0" mavenPublish = "com.vanniktech.maven.publish:0.35.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From 88c7f4c2e3ee77feadad447dd36c7ffa069a7017 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sat, 13 Dec 2025 10:44:56 +0800 Subject: [PATCH 891/941] Add Kdoc for applying resource transformers (#1885) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 91b7319a8..4977aa1d4 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -294,6 +294,12 @@ public abstract class ShadowJar : Jar() { /** * Merge Java services files with [rootPath]. + * + * *Warning*: In most cases, this should be used with the correct [getDuplicatesStrategy] to ensure duplicate service + * files are handled properly. See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * + * @see [getDuplicatesStrategy] */ public open fun mergeServiceFiles(rootPath: String) { mergeServiceFiles { it.path = rootPath } @@ -301,6 +307,12 @@ public abstract class ShadowJar : Jar() { /** * Merge Java services files with [action]. + * + * *Warning*: In most cases, this should be used with the correct [getDuplicatesStrategy] to ensure duplicate service + * files are handled properly. See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * + * @see [getDuplicatesStrategy] */ @JvmOverloads public open fun mergeServiceFiles(action: Action = Action {}) { @@ -309,6 +321,12 @@ public abstract class ShadowJar : Jar() { /** * Merge Groovy extension modules (`META-INF/**/org.codehaus.groovy.runtime.ExtensionModule`). + * + * *Warning*: In most cases, this should be used with the correct [getDuplicatesStrategy] to ensure duplicate extension module + * files are handled properly. See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * + * @see [getDuplicatesStrategy] */ public open fun mergeGroovyExtensionModules() { transform(GroovyExtensionModuleTransformer::class.java, action = {}) @@ -370,6 +388,12 @@ public abstract class ShadowJar : Jar() { /** * Transform resources using a [ResourceTransformer]. + * + * *Warning*: Most of the [ResourceTransformer]s should be used with the correct [getDuplicatesStrategy] to ensure + * duplicate resource files are handled properly. See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * + * @see [getDuplicatesStrategy] */ @JvmOverloads public open fun transform(clazz: Class, action: Action = Action {}) { @@ -378,6 +402,12 @@ public abstract class ShadowJar : Jar() { /** * Transform resources using a [ResourceTransformer]. + * + * *Warning*: Most of the [ResourceTransformer]s should be used with the correct [getDuplicatesStrategy] to ensure + * duplicate resource files are handled properly. See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * + * @see [getDuplicatesStrategy] */ @JvmOverloads public open fun transform(transformer: T, action: Action = Action {}) { @@ -386,6 +416,12 @@ public abstract class ShadowJar : Jar() { /** * Transform resources using a [ResourceTransformer]. + * + * *Warning*: Most of the [ResourceTransformer]s should be used with the correct [getDuplicatesStrategy] to ensure + * duplicate resource files are handled properly. See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * + * @see [getDuplicatesStrategy] */ @JvmSynthetic public inline fun transform(action: Action = Action {}) { From 03b1efa0480e116bc951d4eb86844f14f206e3eb Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 15 Dec 2025 09:38:56 +0800 Subject: [PATCH 892/941] Tidy up doc pages a bit (#1888) * Link code tabs * Overviews for sections * Reorder features --- mkdocs.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 48ab65f44..a74080f91 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -31,9 +31,11 @@ theme: text: 'Lato' code: 'JetBrains Mono' features: + - content.action.edit - content.code.copy - content.code.select - - content.action.edit + - content.tabs.link + - toc.integrate markdown_extensions: - smarty From ca01ea86dbb05ccd478b1160b7d028436a4093e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 06:15:56 +0000 Subject: [PATCH 893/941] Update dependency org.ow2.asm:asm-commons to v9.9.1 (#1889) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b087af082..38f0d0e7a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ apache-commonsCodec = "commons-codec:commons-codec:1.20.0" apache-commonsIo = "commons-io:commons-io:2.21.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.2" apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.11" -asm = "org.ow2.asm:asm-commons:9.9" +asm = "org.ow2.asm:asm-commons:9.9.1" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.14" jdom2 = "org.jdom:jdom2:2.0.6.1" From 25915d11ae9d953d1a4d8555624cca47c6aedaa5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 16 Dec 2025 10:42:33 +0000 Subject: [PATCH 894/941] Update kotlin monorepo to v2.3.0 (#1890) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 38f0d0e7a..673fdf344 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] jdkRelease = "17" minGradle = "9.0.0" -kotlin = "2.3.0-RC3" +kotlin = "2.3.0" moshi = "1.15.2" pluginPublish = "2.0.0" From 872c7e2d6e90a3eb08e5b19fba9adf9045626ef6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:21:18 +0800 Subject: [PATCH 895/941] Update dependency org.apache.logging.log4j:log4j-core to v2.25.3 (#1891) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 673fdf344..07ec3051a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -9,7 +9,7 @@ pluginPublish = "2.0.0" apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsCodec = "commons-codec:commons-codec:1.20.0" apache-commonsIo = "commons-io:commons-io:2.21.0" -apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.2" +apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.3" apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.11" asm = "org.ow2.asm:asm-commons:9.9.1" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. From 3f98c8fe41be8aec92bf05b08fc223cd1356d0aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 17 Dec 2025 12:27:49 +0800 Subject: [PATCH 896/941] Update dependency org.apache.maven:maven-model to v3.9.12 (#1892) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 07ec3051a..10dc1e1fe 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsCodec = "commons-codec:commons-codec:1.20.0" apache-commonsIo = "commons-io:commons-io:2.21.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.3" -apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.11" +apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.12" asm = "org.ow2.asm:asm-commons:9.9.1" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.14" From d2cc4ad708a3f37c40b2574be73693df8adf989a Mon Sep 17 00:00:00 2001 From: Goooler Date: Wed, 17 Dec 2025 14:12:31 +0800 Subject: [PATCH 897/941] Rename modelBuilder to model --- build.gradle.kts | 2 +- gradle/libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 151dc231e..3024a2327 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -175,7 +175,7 @@ testing.suites { } } dependencies { - implementation(libs.apache.maven.modelBuilder) + implementation(libs.apache.maven.model) implementation(libs.moshi) implementation(libs.moshi.kotlin) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 10dc1e1fe..429aef440 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ apache-ant = "org.apache.ant:ant:1.10.15" apache-commonsCodec = "commons-codec:commons-codec:1.20.0" apache-commonsIo = "commons-io:commons-io:2.21.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.3" -apache-maven-modelBuilder = "org.apache.maven:maven-model:3.9.12" +apache-maven-model = "org.apache.maven:maven-model:3.9.12" asm = "org.ow2.asm:asm-commons:9.9.1" # jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.14" From 087af8dc08a367061f664d405fb909d8ca6fbe7d Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 21 Dec 2025 08:28:07 +0800 Subject: [PATCH 898/941] Submit dependency graph on publish-snapshot job --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index ede08b744..0bcae5911 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -54,6 +54,7 @@ jobs: - uses: gradle/actions/setup-gradle@v5 with: cache-read-only: true + dependency-graph: 'generate-submit-and-upload' - run: ./gradlew publishToMavenCentral env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.CENTRAL_PORTAL_USERNAME }} From 2620e88833fb42cf2df2135f700b4ab3dc985d79 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 6 Jan 2026 10:20:51 +0800 Subject: [PATCH 899/941] Fix overrideOutputPathOfNoticeFile (#1896) It's 2026 now. --- .../gradle/plugins/shadow/transformers/TransformersTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index b36d8ecb1..90e0db8c5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -344,7 +344,7 @@ class TransformersTest : BaseTransformerTest() { ) getContent(customNoticeEntry).isEqualTo( """ - Copyright 2006-2025 The Apache Software Foundation + Copyright 2006-2026 The Apache Software Foundation This product includes software developed at The Apache Software Foundation (https://www.apache.org/). From 9fec3d6c669c2192bdcab046b04fa76667cda47f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 6 Jan 2026 10:38:01 +0800 Subject: [PATCH 900/941] Migrate to ktfmt (#1871) --- .editorconfig | 3 - build.gradle.kts | 105 ++- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 4 +- .../plugins/shadow/DocCodeSnippetTest.kt | 8 +- .../shadow/snippet/CodeSnippetExtractor.kt | 25 +- .../gradle/plugins/shadow/snippet/DslLang.kt | 3 +- .../shadow/snippet/GroovyBuildExecutable.kt | 12 +- .../shadow/snippet/KotlinBuildExecutable.kt | 12 +- .../shadow/snippet/SnippetExecutable.kt | 71 +- .../plugins/shadow/ApplicationPluginTest.kt | 200 ++--- .../gradle/plugins/shadow/BasePluginTest.kt | 276 +++---- .../gradle/plugins/shadow/CachingTest.kt | 296 +++----- .../gradle/plugins/shadow/FilteringTest.kt | 135 ++-- .../shadow/FindResourceInClasspathTest.kt | 26 +- .../gradle/plugins/shadow/JavaPluginsTest.kt | 579 +++++++-------- .../plugins/shadow/KotlinPluginsTest.kt | 94 ++- .../gradle/plugins/shadow/MinimizeTest.kt | 250 +++---- .../gradle/plugins/shadow/PublishingTest.kt | 681 ++++++++++-------- .../gradle/plugins/shadow/RelocationTest.kt | 420 ++++++----- .../transformers/AppendingTransformerTest.kt | 92 ++- .../transformers/BaseTransformerTest.kt | 7 +- .../GroovyExtensionModuleTransformerTest.kt | 100 +-- .../PropertiesFileTransformerTest.kt | 152 ++-- .../ServiceFileTransformerTest.kt | 175 +++-- .../shadow/transformers/TransformersTest.kt | 219 +++--- .../XmlAppendingTransformerTest.kt | 68 +- .../shadow/util/AppendableMavenRepository.kt | 151 ++-- .../shadow/util/GradleModuleMetadata.kt | 41 +- .../gradle/plugins/shadow/util/Issue.kt | 11 +- .../gradle/plugins/shadow/util/JarBuilder.kt | 8 +- .../gradle/plugins/shadow/util/JvmLang.kt | 7 +- .../gradle/plugins/shadow/util/RunProcess.kt | 8 +- .../plugins/shadow/ShadowApplicationPlugin.kt | 50 +- .../gradle/plugins/shadow/ShadowBasePlugin.kt | 18 +- .../gradle/plugins/shadow/ShadowExtension.kt | 13 +- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 110 +-- .../gradle/plugins/shadow/ShadowKmpPlugin.kt | 33 +- .../gradle/plugins/shadow/ShadowPlugin.kt | 58 +- .../internal/DefaultDependencyFilter.kt | 8 +- .../shadow/internal/DefaultInheritManifest.kt | 15 +- .../plugins/shadow/internal/GradleCompat.kt | 79 +- .../internal/MinimizeDependencyFilter.kt | 16 +- .../shadow/internal/RelocatorRemapper.kt | 14 +- .../shadow/internal/ReproducibleProperties.kt | 26 +- .../plugins/shadow/internal/UnusedTracker.kt | 11 +- .../gradle/plugins/shadow/internal/Utils.kt | 36 +- .../shadow/relocation/RelocationContext.kt | 8 +- .../plugins/shadow/relocation/Relocator.kt | 12 +- .../shadow/relocation/SimpleRelocator.kt | 115 +-- .../plugins/shadow/tasks/DependencyFilter.kt | 83 +-- .../shadow/tasks/FindResourceInClasspath.kt | 14 +- .../plugins/shadow/tasks/ShadowCopyAction.kt | 242 ++++--- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 368 +++++----- .../ApacheLicenseResourceTransformer.kt | 18 +- .../ApacheNoticeResourceTransformer.kt | 82 ++- .../transformers/AppendingTransformer.kt | 25 +- .../ComponentsXmlResourceTransformer.kt | 45 +- .../DeduplicatingResourceTransformer.kt | 64 +- .../DontIncludeResourceTransformer.kt | 13 +- .../GroovyExtensionModuleTransformer.kt | 49 +- .../IncludeResourceTransformer.kt | 17 +- .../Log4j2PluginsCacheFileTransformer.kt | 15 +- .../ManifestAppenderTransformer.kt | 13 +- .../ManifestResourceTransformer.kt | 32 +- .../MergeLicenseResourceTransformer.kt | 52 +- .../PatternFilterableResourceTransformer.kt | 5 +- .../PreserveFirstFoundResourceTransformer.kt | 28 +- .../transformers/PropertiesFileTransformer.kt | 101 +-- .../transformers/ResourceTransformer.kt | 34 +- .../transformers/ServiceFileTransformer.kt | 37 +- .../shadow/transformers/TransformerContext.kt | 29 +- .../transformers/XmlAppendingTransformer.kt | 42 +- .../plugins/shadow/ShadowPropertiesTest.kt | 347 ++++----- .../internal/ReproduciblePropertiesTest.kt | 102 +-- .../relocation/RelocatorRemapperTest.kt | 65 +- .../shadow/relocation/SimpleRelocatorTest.kt | 265 ++++--- .../ApacheLicenseResourceTransformerTest.kt | 6 +- .../ApacheNoticeResourceTransformerTest.kt | 3 +- .../transformers/AppendingTransformerTest.kt | 3 +- .../transformers/BaseTransformerTest.kt | 49 +- .../ComponentsXmlResourceTransformerTest.kt | 15 +- .../DeduplicatingResourceTransformerTest.kt | 137 ++-- .../Log4j2PluginsCacheFileTransformerTest.kt | 32 +- .../ManifestAppenderTransformerTest.kt | 47 +- .../MergeLicenseResourceTransformerTest.kt | 191 ++--- .../PropertiesFileTransformerTest.kt | 341 ++++----- .../ServiceFileTransformerTest.kt | 33 +- .../plugins/shadow/testkit/GradleRunner.kt | 65 +- .../gradle/plugins/shadow/testkit/JarPath.kt | 56 +- .../gradle/plugins/shadow/testkit/Resource.kt | 5 +- .../gradle/plugins/shadow/testkit/Strings.kt | 6 +- 92 files changed, 4098 insertions(+), 3941 deletions(-) diff --git a/.editorconfig b/.editorconfig index 379e533b4..a40d649a0 100755 --- a/.editorconfig +++ b/.editorconfig @@ -16,9 +16,6 @@ ij_kotlin_line_break_after_multiline_when_entry = false ij_kotlin_name_count_to_use_star_import = 2147483647 ij_kotlin_name_count_to_use_star_import_for_members = 2147483647 ij_kotlin_packages_to_use_import_on_demand = unset -ktlint_code_style = intellij_idea -ktlint_standard_function-expression-body = disabled -ktlint_standard_mixed-condition-operators = disabled [*.md] trim_trailing_whitespace = false diff --git a/build.gradle.kts b/build.gradle.kts index 3024a2327..d335434ad 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -19,21 +19,16 @@ plugins { } version = providers.gradleProperty("VERSION_NAME").get() + group = providers.gradleProperty("GROUP").get() + description = providers.gradleProperty("POM_DESCRIPTION").get() -dokka { - dokkaPublications.html { - outputDirectory = rootDir.resolve("docs/api") - } -} +dokka { dokkaPublications.html { outputDirectory = rootDir.resolve("docs/api") } } kotlin { explicitApi() - @OptIn(ExperimentalAbiValidation::class) - abiValidation { - enabled = true - } + @OptIn(ExperimentalAbiValidation::class) abiValidation { enabled = true } compilerOptions { allWarningsAsErrors = true // https://docs.gradle.org/current/userguide/compatibility.html#kotlin @@ -56,18 +51,15 @@ lint { } spotless { - kotlin { - ktlint(libs.ktlint.get().version) - } - kotlinGradle { - ktlint(libs.ktlint.get().version) - } + kotlin { ktfmt(libs.ktfmt.get().version).googleStyle() } + kotlinGradle { ktfmt(libs.ktfmt.get().version).googleStyle() } } -val testPluginClasspath by configurations.registering { - isCanBeResolved = true - description = "Plugins used in integration tests could be resolved in classpath." -} +val testPluginClasspath by + configurations.registering { + isCanBeResolved = true + description = "Plugins used in integration tests could be resolved in classpath." + } val testKit by sourceSets.creating val testKitImplementation by configurations.getting @@ -77,18 +69,18 @@ configurations.configureEach { API_ELEMENTS_CONFIGURATION_NAME, RUNTIME_ELEMENTS_CONFIGURATION_NAME, JAVADOC_ELEMENTS_CONFIGURATION_NAME, - SOURCES_ELEMENTS_CONFIGURATION_NAME, - -> outgoing { - // Main/current capability. - capability("com.gradleup.shadow:shadow-gradle-plugin:$version") - - // Historical capabilities. - capability("io.github.goooler.shadow:shadow-gradle-plugin:$version") - capability("com.github.johnrengelman:shadow:$version") - capability("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:$version") - capability("gradle.plugin.com.github.johnrengelman:shadow:$version") - capability("com.github.jengelman.gradle.plugins:shadow:$version") - } + SOURCES_ELEMENTS_CONFIGURATION_NAME -> + outgoing { + // Main/current capability. + capability("com.gradleup.shadow:shadow-gradle-plugin:$version") + + // Historical capabilities. + capability("io.github.goooler.shadow:shadow-gradle-plugin:$version") + capability("com.github.johnrengelman:shadow:$version") + capability("gradle.plugin.com.github.jengelman.gradle.plugins:shadow:$version") + capability("gradle.plugin.com.github.johnrengelman:shadow:$version") + capability("com.github.jengelman.gradle.plugins:shadow:$version") + } } } @@ -108,11 +100,12 @@ configurations.named(API_ELEMENTS_CONFIGURATION_NAME) { ) } -val testGradleVersion: String = providers.gradleProperty("testGradleVersion").orNull.let { - val value = if (it == null || it == "current") GradleVersion.current().version else it - logger.lifecycle("Using Gradle $value in tests") - value -} +val testGradleVersion: String = + providers.gradleProperty("testGradleVersion").orNull.let { + val value = if (it == null || it == "current") GradleVersion.current().version else it + logger.lifecycle("Using Gradle $value in tests") + value + } dependencies { compileOnly(libs.develocity) @@ -141,17 +134,15 @@ dependencies { } testing.suites { - getByName("test") { - dependencies { - implementation(libs.xmlunit) - } - } + getByName("test") { dependencies { implementation(libs.xmlunit) } } register("documentTest") { targets.configureEach { testTask { - val docsDir = file("docs").also { - if (!it.exists() || !it.isDirectory) error("Docs dir $it does not exist or is not a directory.") - } + val docsDir = + file("docs").also { + if (!it.exists() || !it.isDirectory) + error("Docs dir $it does not exist or is not a directory.") + } // Add docs as an input directory to trigger ManualCodeSnippetTests re-run on changes. inputs.dir(docsDir) systemProperty("DOCS_DIR", docsDir.absolutePath) @@ -215,10 +206,7 @@ gradlePlugin { } } - testSourceSets( - sourceSets["functionalTest"], - sourceSets["documentTest"], - ) + testSourceSets(sourceSets["functionalTest"], sourceSets["documentTest"]) } // This part should be placed after testing.suites to ensure the test sourceSets are created. @@ -234,11 +222,7 @@ tasks.withType().configureEach { options.release = libs.versions.jdkRelease.get().toInt() } -tasks.pluginUnderTestMetadata { - pluginClasspath.from( - testPluginClasspath, - ) -} +tasks.pluginUnderTestMetadata { pluginClasspath.from(testPluginClasspath) } tasks.validatePlugins { // TODO: https://github.com/gradle/gradle/issues/22600 @@ -254,11 +238,12 @@ tasks.check { } tasks.clean { - delete += listOf( - projectDir.resolve(".gradle"), - projectDir.resolve(".kotlin"), - dokka.dokkaPublications.html.map { it.outputDirectory }, - // Generated by MkDocs. - rootDir.resolve("site"), - ) + delete += + listOf( + projectDir.resolve(".gradle"), + projectDir.resolve(".kotlin"), + dokka.dokkaPublications.html.map { it.outputDirectory }, + // Generated by MkDocs. + rootDir.resolve("site"), + ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 429aef440..c3ed3ce93 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.r androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. -ktlint = "com.pinterest.ktlint:ktlint-cli:1.8.0" +ktfmt = "com.facebook:ktfmt:0.59" junit-bom = "org.junit:junit-bom:6.0.1" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" diff --git a/settings.gradle.kts b/settings.gradle.kts index af496d121..93027b77d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,9 +12,7 @@ pluginManagement { } } -plugins { - id("com.gradle.develocity") version "4.3" -} +plugins { id("com.gradle.develocity") version "4.3" } develocity { buildScan { diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt index bfc913a80..d9b271dc2 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/DocCodeSnippetTest.kt @@ -12,13 +12,9 @@ class DocCodeSnippetTest { @TestFactory fun provideDynamicTests(@TempDir root: Path): List { - val langExecutables = DslLang.entries.map { executor -> - CodeSnippetExtractor.extract(executor) - } + val langExecutables = DslLang.entries.map { executor -> CodeSnippetExtractor.extract(executor) } - check(langExecutables.sumOf { it.size } > 0) { - "No code snippets found." - } + check(langExecutables.sumOf { it.size } > 0) { "No code snippets found." } check(langExecutables.size == DslLang.entries.size) { "We must provide build script snippets for all languages." } diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt index b63095985..b5d253816 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/CodeSnippetExtractor.kt @@ -13,28 +13,21 @@ object CodeSnippetExtractor { private val docRoot = Path(System.getProperty("DOCS_DIR")) @OptIn(ExperimentalPathApi::class) - private val markdownPaths = docRoot.walk() - .filter { it.name.endsWith(".md", ignoreCase = true) } - .toList() + private val markdownPaths = + docRoot.walk().filter { it.name.endsWith(".md", ignoreCase = true) }.toList() fun extract(lang: DslLang): List { - return markdownPaths.flatMap { path -> - createExecutables(lang, path) - } + return markdownPaths.flatMap { path -> createExecutables(lang, path) } } - private fun createExecutables( - lang: DslLang, - markdownPath: Path, - ): List { + private fun createExecutables(lang: DslLang, markdownPath: Path): List { val relativeDocPath = markdownPath.relativeTo(docRoot).toString() return createSnippets(markdownPath.readText(), lang).map { (lineNumber, snippet) -> - SnippetExecutable.create( - lang, - snippet, - "$relativeDocPath:$lineNumber", - ) { - RuntimeException("The error line in the doc is near ${markdownPath.toUri()}:$lineNumber", it) + SnippetExecutable.create(lang, snippet, "$relativeDocPath:$lineNumber") { + RuntimeException( + "The error line in the doc is near ${markdownPath.toUri()}:$lineNumber", + it, + ) } } } diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt index 8179417cc..87384d08c 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/DslLang.kt @@ -2,8 +2,7 @@ package com.github.jengelman.gradle.plugins.shadow.snippet enum class DslLang { Kotlin, - Groovy, - ; + Groovy; override fun toString(): String = name.lowercase() } diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt index 044d312e7..c3bf8e672 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/GroovyBuildExecutable.kt @@ -10,16 +10,20 @@ class GroovyBuildExecutable( override val buildScriptName: String = "build.gradle" - override val pluginsBlock: String = """ + override val pluginsBlock: String = + """ plugins { id 'java' id 'com.gradleup.shadow' } - """.trimIndent() + """ + .trimIndent() - override val assembleDependsOn: String = """ + override val assembleDependsOn: String = + """ tasks.named('assemble') { dependsOn tasks.withType(Jar) // ShadowJar is a subtype of Jar. } - """.trimIndent() + """ + .trimIndent() } diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt index 43e04aabc..7dcdfe2fb 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/KotlinBuildExecutable.kt @@ -10,16 +10,20 @@ class KotlinBuildExecutable( override val buildScriptName: String = "build.gradle.kts" - override val pluginsBlock: String = """ + override val pluginsBlock: String = + """ plugins { java id("com.gradleup.shadow") } - """.trimIndent() + """ + .trimIndent() - override val assembleDependsOn: String = """ + override val assembleDependsOn: String = + """ tasks.named("assemble") { dependsOn(tasks.withType(Jar::class.java)) // ShadowJar is a subtype of Jar. } - """.trimIndent() + """ + .trimIndent() } diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index 88a30614d..3d02c49ec 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -19,9 +19,7 @@ sealed class SnippetExecutable : Executable { abstract val snippet: String - /** - * Unique name for the test, formatted as `publishing/README.md:10`. - */ + /** Unique name for the test, formatted as `publishing/README.md:10`. */ abstract val displayName: String abstract val exceptionTransformer: (Throwable) -> Throwable @@ -36,8 +34,10 @@ sealed class SnippetExecutable : Executable { } private fun execute(projectRoot: Path, snippet: String) { - projectRoot.resolve("settings.gradle").writeText( - """ + projectRoot + .resolve("settings.gradle") + .writeText( + """ dependencyResolutionManagement { repositories { mavenLocal() @@ -50,8 +50,9 @@ sealed class SnippetExecutable : Executable { include ':api', ':main' rootProject.name = 'snippet' enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS' - """.trimIndent(), - ) + """ + .trimIndent() + ) val apiScript = buildString { append(pluginsBlock) @@ -61,23 +62,25 @@ sealed class SnippetExecutable : Executable { projectRoot.addSubProject("api", apiScript) val (imports, withoutImports) = importsExtractor(snippet) - val mainScript = buildString { - append(imports) - append(lineSeparator) - // All buildscript {} blocks must appear before any plugins {} blocks in the script. - if (withoutImports.contains("buildscript {")) { - append(withoutImports) - } else { - if (!withoutImports.contains("plugins {")) { - append(pluginsBlock) + val mainScript = + buildString { + append(imports) + append(lineSeparator) + // All buildscript {} blocks must appear before any plugins {} blocks in the script. + if (withoutImports.contains("buildscript {")) { + append(withoutImports) + } else { + if (!withoutImports.contains("plugins {")) { + append(pluginsBlock) + append(lineSeparator) + } + append(withoutImports) + } + append(lineSeparator) + append(assembleDependsOn) append(lineSeparator) } - append(withoutImports) - } - append(lineSeparator) - append(assembleDependsOn) - append(lineSeparator) - }.trimIndent() + .trimIndent() projectRoot.addSubProject("main", mainScript) projectRoot.resolve("main/foo.jar").createFile().also { // Dummy JAR file to ensure the project can be built. @@ -90,20 +93,19 @@ sealed class SnippetExecutable : Executable { try { gradleRunner( - projectDir = projectRoot, - arguments = listOf("build", "--stacktrace"), - warningsAsErrors = mainScript.toWarningsAsErrors(), - ).build().assertNoDeprecationWarnings() + projectDir = projectRoot, + arguments = listOf("build", "--stacktrace"), + warningsAsErrors = mainScript.toWarningsAsErrors(), + ) + .build() + .assertNoDeprecationWarnings() } catch (t: Throwable) { throw RuntimeException("Failed to execute snippet:\n\n$mainScript", t) } } private fun Path.addSubProject(project: String, buildScriptText: String) { - resolve(project) - .createDirectory() - .resolve(buildScriptName) - .writeText(buildScriptText) + resolve(project).createDirectory().resolve(buildScriptName).writeText(buildScriptText) } private fun importsExtractor(snippet: String): Pair { @@ -129,9 +131,10 @@ sealed class SnippetExecutable : Executable { snippet: String, testName: String, exceptionTransformer: (Throwable) -> Throwable, - ): SnippetExecutable = when (lang) { - DslLang.Groovy -> GroovyBuildExecutable(snippet, testName, exceptionTransformer) - DslLang.Kotlin -> KotlinBuildExecutable(snippet, testName, exceptionTransformer) - } + ): SnippetExecutable = + when (lang) { + DslLang.Groovy -> GroovyBuildExecutable(snippet, testName, exceptionTransformer) + DslLang.Kotlin -> KotlinBuildExecutable(snippet, testName, exceptionTransformer) + } } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt index 3534a320e..bfcc48a8e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/ApplicationPluginTest.kt @@ -36,37 +36,45 @@ class ApplicationPluginTest : BasePluginTest() { @DisabledOnOs( OS.WINDOWS, architectures = ["aarch64"], - disabledReason = "Cannot use toolchain on Windows ARM64", // TODO: remove when min Gradle is bumped to 9.2+ + disabledReason = + "Cannot use toolchain on Windows ARM64", // TODO: remove when min Gradle is bumped to 9.2+ ) @Test fun integrationWithApplicationPluginAndJavaToolchains() { prepare( mainClassWithImports = true, dependenciesBlock = "implementation 'junit:junit:3.8.2'", - projectBlock = """ + projectBlock = + """ java { toolchain.languageVersion = JavaLanguageVersion.of(17) } - """.trimIndent(), - settingsBlock = """ + """ + .trimIndent(), + settingsBlock = + """ plugins { id 'org.gradle.toolchains.foojay-resolver-convention' } - """.trimIndent(), - runShadowBlock = $$""" + """ + .trimIndent(), + runShadowBlock = + $$""" doFirst { logger.lifecycle("Running application with JDK ${it.javaLauncher.get().metadata.languageVersion.asInt()}") } - """.trimIndent(), + """ + .trimIndent(), ) val result = runWithSuccess(runShadowPath) - assertThat(result.output).contains( - "Running application with JDK 17", - "Hello, World! (foo) from Main", - "Refs: junit.framework.Test", - ) + assertThat(result.output) + .contains( + "Running application with JDK 17", + "Hello, World! (foo) from Main", + "Refs: junit.framework.Test", + ) } @Test @@ -74,17 +82,19 @@ class ApplicationPluginTest : BasePluginTest() { prepare( mainClassWithImports = true, dependenciesBlock = "implementation 'junit:junit:3.8.2'", - applicationBlock = "applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED']", + applicationBlock = + "applicationDefaultJvmArgs = ['--add-opens=java.base/java.lang=ALL-UNNAMED']", ) runWithSuccess(installShadowDistPath) val installPath = path("build/install/") - assertThat(installPath.walkEntries()).containsOnly( - "myapp-shadow/bin/myapp", - "myapp-shadow/bin/myapp.bat", - "myapp-shadow/lib/myapp-1.0-all.jar", - ) + assertThat(installPath.walkEntries()) + .containsOnly( + "myapp-shadow/bin/myapp", + "myapp-shadow/bin/myapp.bat", + "myapp-shadow/lib/myapp-1.0-all.jar", + ) commonAssertions( jarPath("myapp-shadow/lib/myapp-1.0-all.jar", installPath), @@ -94,25 +104,26 @@ class ApplicationPluginTest : BasePluginTest() { val unixScript = path("myapp-shadow/bin/myapp", installPath) val winScript = path("myapp-shadow/bin/myapp.bat", installPath) - assertThat(unixScript.readText()).contains( - $$"CLASSPATH=$APP_HOME/lib/myapp-1.0-all.jar", - $$"exec \"$JAVACMD\" \"$@\"", - "DEFAULT_JVM_OPTS='\"--add-opens=java.base/java.lang=ALL-UNNAMED\"'", - ) - assertThat(winScript.readText()).contains( - "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", - "set DEFAULT_JVM_OPTS=\"--add-opens=java.base/java.lang=ALL-UNNAMED\"", - ) + assertThat(unixScript.readText()) + .contains( + $$"CLASSPATH=$APP_HOME/lib/myapp-1.0-all.jar", + $$"exec \"$JAVACMD\" \"$@\"", + "DEFAULT_JVM_OPTS='\"--add-opens=java.base/java.lang=ALL-UNNAMED\"'", + ) + assertThat(winScript.readText()) + .contains( + "set CLASSPATH=%APP_HOME%\\lib\\myapp-1.0-all.jar", + "set DEFAULT_JVM_OPTS=\"--add-opens=java.base/java.lang=ALL-UNNAMED\"", + ) - val runningOutput = if (isWindows) { - runProcess(winScript.toString(), "bar") - } else { - runProcess(unixScript.toString(), "bar") - } - assertThat(runningOutput).contains( - "Hello, World! (bar) from Main", - "Refs: junit.framework.Test", - ) + val runningOutput = + if (isWindows) { + runProcess(winScript.toString(), "bar") + } else { + runProcess(unixScript.toString(), "bar") + } + assertThat(runningOutput) + .contains("Hello, World! (bar) from Main", "Refs: junit.framework.Test") } @Test @@ -124,20 +135,20 @@ class ApplicationPluginTest : BasePluginTest() { commonAssertions(jarPath("build/install/myapp-shadow/lib/myapp-1.0-all.jar")) } - @Issue( - "https://github.com/GradleUp/shadow/issues/613", - ) + @Issue("https://github.com/GradleUp/shadow/issues/613") @Test fun overrideMainClassAttrInManifestBlock() { val main2ClassEntry = writeClass(className = "Main2") prepare( - projectBlock = """ + projectBlock = + """ shadowJar { manifest { attributes '$mainClassAttributeKey': 'my.Main2' } } - """.trimIndent(), + """ + .trimIndent() ) var result = runWithSuccess(runShadowPath) @@ -158,10 +169,11 @@ class ApplicationPluginTest : BasePluginTest() { projectScript.appendText( """ - run { - args 'bar' - } - """.trimIndent(), + run { + args 'bar' + } + """ + .trimIndent() ) result = runWithSuccess(":run") @@ -177,7 +189,8 @@ class ApplicationPluginTest : BasePluginTest() { $shadowJarTask { mainClass = 'my.Main2' // Different from application.mainClass. } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(runShadowPath) // Run without errors. @@ -193,9 +206,7 @@ class ApplicationPluginTest : BasePluginTest() { val result = runWithFailure(runShadowPath) - assertThat(result.output).contains( - "no main manifest attribute, in", - ) + assertThat(result.output).contains("no main manifest attribute, in") } @Test @@ -203,18 +214,22 @@ class ApplicationPluginTest : BasePluginTest() { path("extra/echo.sh").writeText("echo 'Hello, World!'") path("some/dir/hello.txt").writeText("'Hello, World!'") prepare( - projectBlock = """ + projectBlock = + """ distributions.named('$DISTRIBUTION_NAME') { contents.from('extra/echo.sh') { into 'extra' } } - """.trimIndent(), - applicationBlock = """ + """ + .trimIndent(), + applicationBlock = + """ applicationDistribution.from('some/dir') { include '*.txt' } - """.trimIndent(), + """ + .trimIndent(), ) runWithSuccess(shadowDistZipPath) @@ -222,17 +237,16 @@ class ApplicationPluginTest : BasePluginTest() { val zipPath = path("build/distributions/myapp-shadow-1.0.zip") ZipFile(zipPath.toFile()).use { zip -> val entries = zip.entries().toList().filter { !it.isDirectory }.map { it.name } - assertThat(entries).containsOnly( - "myapp-shadow-1.0/bin/myapp", - "myapp-shadow-1.0/bin/myapp.bat", - "myapp-shadow-1.0/lib/myapp-1.0-all.jar", - "myapp-shadow-1.0/extra/echo.sh", - "myapp-shadow-1.0/hello.txt", - ) - assertThat(zip.getContent("myapp-shadow-1.0/extra/echo.sh")) - .isEqualTo("echo 'Hello, World!'") - assertThat(zip.getContent("myapp-shadow-1.0/hello.txt")) - .isEqualTo("'Hello, World!'") + assertThat(entries) + .containsOnly( + "myapp-shadow-1.0/bin/myapp", + "myapp-shadow-1.0/bin/myapp.bat", + "myapp-shadow-1.0/lib/myapp-1.0-all.jar", + "myapp-shadow-1.0/extra/echo.sh", + "myapp-shadow-1.0/hello.txt", + ) + assertThat(zip.getContent("myapp-shadow-1.0/extra/echo.sh")).isEqualTo("echo 'Hello, World!'") + assertThat(zip.getContent("myapp-shadow-1.0/hello.txt")).isEqualTo("'Hello, World!'") } } @@ -246,14 +260,14 @@ class ApplicationPluginTest : BasePluginTest() { val zipPath = path("build/distributions/myapp-shadow-1.0.zip") ZipFile(zipPath.toFile()).use { zip -> val entries = zip.entries().toList().filter { !it.isDirectory }.map { it.name } - assertThat(entries).containsOnly( - "myapp-shadow-1.0/bin/myapp", - "myapp-shadow-1.0/bin/myapp.bat", - "myapp-shadow-1.0/lib/myapp-1.0-all.jar", - "myapp-shadow-1.0/echo.sh", - ) - assertThat(zip.getContent("myapp-shadow-1.0/echo.sh")) - .isEqualTo("echo 'Hello, World!'") + assertThat(entries) + .containsOnly( + "myapp-shadow-1.0/bin/myapp", + "myapp-shadow-1.0/bin/myapp.bat", + "myapp-shadow-1.0/lib/myapp-1.0-all.jar", + "myapp-shadow-1.0/echo.sh", + ) + assertThat(zip.getContent("myapp-shadow-1.0/echo.sh")).isEqualTo("echo 'Hello, World!'") } } @@ -263,27 +277,31 @@ class ApplicationPluginTest : BasePluginTest() { val executableDirs = "sbin" to "sbin" prepare( - applicationBlock = """ + applicationBlock = + """ applicationName = '${applicationNames.first}' executableDir = '${executableDirs.first}' - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(installShadowDistPath, shadowDistZipPath) - assertThat(path("build/install/").walkEntries()).containsOnly( - "${applicationNames.second}-shadow/${executableDirs.second}/${applicationNames.second}", - "${applicationNames.second}-shadow/${executableDirs.second}/${applicationNames.second}.bat", - "${applicationNames.second}-shadow/lib/myapp-1.0-all.jar", - ) + assertThat(path("build/install/").walkEntries()) + .containsOnly( + "${applicationNames.second}-shadow/${executableDirs.second}/${applicationNames.second}", + "${applicationNames.second}-shadow/${executableDirs.second}/${applicationNames.second}.bat", + "${applicationNames.second}-shadow/lib/myapp-1.0-all.jar", + ) val zipPath = path("build/distributions/${applicationNames.second}-shadow-1.0.zip") ZipFile(zipPath.toFile()).use { zip -> val entries = zip.entries().toList().filter { !it.isDirectory }.map { it.name } - assertThat(entries).containsOnly( - "${applicationNames.second}-shadow-1.0/${executableDirs.second}/${applicationNames.second}", - "${applicationNames.second}-shadow-1.0/${executableDirs.second}/${applicationNames.second}.bat", - "${applicationNames.second}-shadow-1.0/lib/myapp-1.0-all.jar", - ) + assertThat(entries) + .containsOnly( + "${applicationNames.second}-shadow-1.0/${executableDirs.second}/${applicationNames.second}", + "${applicationNames.second}-shadow-1.0/${executableDirs.second}/${applicationNames.second}.bat", + "${applicationNames.second}-shadow-1.0/lib/myapp-1.0-all.jar", + ) } } @@ -312,13 +330,14 @@ class ApplicationPluginTest : BasePluginTest() { args 'foo' $runShadowBlock } - """.trimIndent() + lineSeparator, + """ + .trimIndent() + lineSeparator ) settingsScript.writeText( getDefaultSettingsBuildScript( startBlock = settingsBlock, endBlock = "rootProject.name = 'myapp'", - ), + ) ) } @@ -337,9 +356,10 @@ class ApplicationPluginTest : BasePluginTest() { private companion object { @OptIn(ExperimentalPathApi::class) - fun Path.walkEntries(includeDirs: Boolean = false): Sequence = walk() - .filter { includeDirs || it.isRegularFile() } - .map { it.relativeTo(this) } - .map { it.invariantSeparatorsPathString } + fun Path.walkEntries(includeDirs: Boolean = false): Sequence = + walk() + .filter { includeDirs || it.isRegularFile() } + .map { it.relativeTo(this) } + .map { it.invariantSeparatorsPathString } } } 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 8e8fdebfe..416e97836 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 @@ -48,74 +48,75 @@ abstract class BasePluginTest { @TempDir lateinit var projectRoot: Path private set + lateinit var localRepo: AppendableMavenRepository private set lateinit var artifactAJar: Path private set + lateinit var artifactBJar: Path private set - val projectScript: Path get() = path("build.gradle") - val settingsScript: Path get() = path("settings.gradle") - val outputJar: JarPath get() = jarPath("build/libs/my-1.0.jar") - open val outputShadowedJar: JarPath get() = jarPath("build/libs/my-1.0-all.jar") - val outputServerShadowedJar: JarPath get() = jarPath("server/build/libs/server-1.0-all.jar") + val projectScript: Path + get() = path("build.gradle") + + val settingsScript: Path + get() = path("settings.gradle") + + val outputJar: JarPath + get() = jarPath("build/libs/my-1.0.jar") + + open val outputShadowedJar: JarPath + get() = jarPath("build/libs/my-1.0-all.jar") + + val outputServerShadowedJar: JarPath + get() = jarPath("server/build/libs/server-1.0-all.jar") @BeforeAll fun beforeAll() { - localRepo = AppendableMavenRepository( - root = createTempDirectory().resolve("local-maven-repo").createDirectories(), - ).apply { - jarModule("junit", "junit", "3.8.2") { - useJar(junitJar) - } - val a = jarModule("my", "a", "1.0") { - buildJar { - insert("a.properties", "a") - insert("a2.properties", "a2") - } - } - val b = jarModule("my", "b", "1.0") { - buildJar { - insert("b.properties", "b") - } - } - val c = jarModule("my", "c", "1.0") { - buildJar { - insert("c.properties", "c") - } - } - val d = jarModule("my", "d", "1.0") { - buildJar { - insert("d.properties", "d") - } - // Depends on c but c does not depend on d. - addDependency(c) - } - val e = jarModule("my", "e", "1.0") { - buildJar { - insert("e.properties", "e") - } - // Circular dependency with f. - addDependency("my:f:1.0") - } - val f = jarModule("my", "f", "1.0") { - buildJar { - insert("f.properties", "f") + localRepo = + AppendableMavenRepository( + root = createTempDirectory().resolve("local-maven-repo").createDirectories() + ) + .apply { + jarModule("junit", "junit", "3.8.2") { useJar(junitJar) } + val a = + jarModule("my", "a", "1.0") { + buildJar { + insert("a.properties", "a") + insert("a2.properties", "a2") + } + } + val b = jarModule("my", "b", "1.0") { buildJar { insert("b.properties", "b") } } + val c = jarModule("my", "c", "1.0") { buildJar { insert("c.properties", "c") } } + val d = + jarModule("my", "d", "1.0") { + buildJar { insert("d.properties", "d") } + // Depends on c but c does not depend on d. + addDependency(c) + } + val e = + jarModule("my", "e", "1.0") { + buildJar { insert("e.properties", "e") } + // Circular dependency with f. + addDependency("my:f:1.0") + } + val f = + jarModule("my", "f", "1.0") { + buildJar { insert("f.properties", "f") } + // Circular dependency with e. + addDependency(e) + } + bomModule("my", "bom", "1.0") { + addDependency(a) + addDependency(b) + addDependency(c) + addDependency(d) + addDependency(e) + addDependency(f) + } } - // Circular dependency with e. - addDependency(e) - } - bomModule("my", "bom", "1.0") { - addDependency(a) - addDependency(b) - addDependency(c) - addDependency(d) - addDependency(e) - addDependency(f) - } - } localRepo.publish() artifactAJar = path("my/a/1.0/a-1.0.jar", parent = localRepo.root) @@ -135,8 +136,7 @@ abstract class BasePluginTest { @AfterAll fun afterAll() { - @OptIn(ExperimentalPathApi::class) - localRepo.root.deleteRecursively() + @OptIn(ExperimentalPathApi::class) localRepo.root.deleteRecursively() } fun getDefaultProjectBuildScript( @@ -154,13 +154,16 @@ abstract class BasePluginTest { } $groupInfo $versionInfo - """.trimIndent() + lineSeparator + """ + .trimIndent() + lineSeparator } fun getDefaultSettingsBuildScript( startBlock: String = "", - // Use a test-specific build cache directory. This ensures that we'll only use cached outputs generated during - // this test, and we won't accidentally use cached outputs from a different test or a different build. + // Use a test-specific build cache directory. This ensures that we'll only use cached outputs + // generated during + // this test, and we won't accidentally use cached outputs from a different test or a different + // build. // https://docs.gradle.org/current/userguide/build_cache.html#sec:build_cache_configure_local buildCacheBlock: String = "local { directory = file('build-cache') }", endBlock: String = "rootProject.name = 'my'", @@ -178,7 +181,8 @@ abstract class BasePluginTest { } enableFeaturePreview 'TYPESAFE_PROJECT_ACCESSORS' $endBlock - """.trimIndent() + lineSeparator + """ + .trimIndent() + lineSeparator } fun jarPath(relative: String, parent: Path = projectRoot): JarPath { @@ -202,20 +206,16 @@ abstract class BasePluginTest { return JarBuilder(path("temp/$relative")).apply(builder).write() } - fun runWithSuccess( - vararg arguments: String, - block: GradleRunner.() -> Unit = {}, - ): BuildResult { + fun runWithSuccess(vararg arguments: String, block: GradleRunner.() -> Unit = {}): BuildResult { return runner(arguments = arguments.toList(), block = block) - .build().assertNoDeprecationWarnings() + .build() + .assertNoDeprecationWarnings() } - fun runWithFailure( - vararg arguments: String, - block: GradleRunner.() -> Unit = {}, - ): BuildResult { + fun runWithFailure(vararg arguments: String, block: GradleRunner.() -> Unit = {}): BuildResult { return runner(arguments = arguments.toList(), block = block) - .buildAndFail().assertNoDeprecationWarnings() + .buildAndFail() + .assertNoDeprecationWarnings() } fun writeClass( @@ -240,7 +240,8 @@ abstract class BasePluginTest { System.out.println($classRef); } } - """.trimIndent() + """ + .trimIndent() } JvmLang.Kotlin -> { val imports = if (withImports) "import junit.framework.Test" else "" @@ -255,7 +256,8 @@ abstract class BasePluginTest { println(content) println($classRef) } - """.trimIndent() + """ + .trimIndent() } } }, @@ -265,41 +267,46 @@ abstract class BasePluginTest { return "$basePath.class" } - fun writeClientAndServerModules( - clientShadowed: Boolean = false, - serverShadowBlock: String = "", - ) { + fun writeClientAndServerModules(clientShadowed: Boolean = false, serverShadowBlock: String = "") { settingsScript.appendText( """ - include 'client', 'server' - """.trimIndent(), + include 'client', 'server' + """ + .trimIndent() ) projectScript.writeText("") - path("client/src/main/java/client/Client.java").writeText( - """ + path("client/src/main/java/client/Client.java") + .writeText( + """ package client; public class Client {} - """.trimIndent(), - ) - path("client/build.gradle").writeText( - """ + """ + .trimIndent() + ) + path("client/build.gradle") + .writeText( + """ ${getDefaultProjectBuildScript("java")} dependencies { implementation 'junit:junit:3.8.2' } - """.trimIndent() + lineSeparator, - ) - - path("server/src/main/java/server/Server.java").writeText( """ + .trimIndent() + lineSeparator + ) + + path("server/src/main/java/server/Server.java") + .writeText( + """ package server; import client.Client; public class Server {} - """.trimIndent(), - ) - path("server/build.gradle").writeText( - """ + """ + .trimIndent() + ) + path("server/build.gradle") + .writeText( + """ ${getDefaultProjectBuildScript("java")} dependencies { implementation project(':client') @@ -307,27 +314,34 @@ abstract class BasePluginTest { $shadowJarTask { $serverShadowBlock } - """.trimIndent() + lineSeparator, - ) + """ + .trimIndent() + lineSeparator + ) if (!clientShadowed) return - path("client/build.gradle").appendText( - """ + path("client/build.gradle") + .appendText( + """ $shadowJarTask { relocate 'junit.framework', 'client.junit.framework' } - """.trimIndent() + lineSeparator, - ) - path("server/src/main/java/server/Server.java").writeText( """ + .trimIndent() + lineSeparator + ) + path("server/src/main/java/server/Server.java") + .writeText( + """ package server; import client.Client; import client.junit.framework.Test; public class Server {} - """.trimIndent(), - ) - val replaced = path("server/build.gradle").readText() - .replace("project(':client')", "project(path: ':client', configuration: 'shadow')") + """ + .trimIndent() + ) + val replaced = + path("server/build.gradle") + .readText() + .replace("project(':client')", "project(path: ':client', configuration: 'shadow')") path("server/build.gradle").writeText(replaced) } @@ -343,11 +357,13 @@ abstract class BasePluginTest { } } } - """.trimIndent() + lineSeparator, + """ + .trimIndent() + lineSeparator ) - path("src/main/java/my/plugin/MyPlugin.java").writeText( - """ + path("src/main/java/my/plugin/MyPlugin.java") + .writeText( + """ package my.plugin; import org.gradle.api.Plugin; import org.gradle.api.Project; @@ -356,19 +372,18 @@ abstract class BasePluginTest { System.out.println("MyPlugin: Hello, World!"); } } - """.trimIndent(), - ) + """ + .trimIndent() + ) } - private fun runner( - arguments: Iterable, - block: GradleRunner.() -> Unit, - ): GradleRunner { - val warningsAsErrors = try { - projectScript.readText().toWarningsAsErrors() - } catch (_: UninitializedPropertyAccessException) { - true // Default warning mode if projectScript is not initialized yet. - } + private fun runner(arguments: Iterable, block: GradleRunner.() -> Unit): GradleRunner { + val warningsAsErrors = + try { + projectScript.readText().toWarningsAsErrors() + } catch (_: UninitializedPropertyAccessException) { + true // Default warning mode if projectScript is not initialized yet. + } return gradleRunner( projectDir = projectRoot, arguments = commonGradleArgs + arguments, @@ -392,17 +407,19 @@ abstract class BasePluginTest { val entriesInB = arrayOf("b.properties") val entriesInAB = entriesInA + entriesInB val junitJar: Path = requireResourceAsPath("junit-3.8.2.jar") - val junitRawEntries: List = JarPath(junitJar) - .use { it.entries().toList() } - .filterNot { - // This entry is not present in the jar file. - it.name == "junit3.8.2/" - } + val junitRawEntries: List = + JarPath(junitJar) + .use { it.entries().toList() } + .filterNot { + // This entry is not present in the jar file. + it.name == "junit3.8.2/" + } val junitEntries: Array = junitRawEntries.map { it.name }.toTypedArray() const val manifestEntry = "META-INF/MANIFEST.MF" val manifestEntries = arrayOf("META-INF/", manifestEntry) - val shadowJarTask: String = "tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name})" + val shadowJarTask: String = + "tasks.named('$SHADOW_JAR_TASK_NAME', ${ShadowJar::class.java.name})" const val runShadowTask = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)" const val jarTask = "tasks.named('jar', Jar)" @@ -414,7 +431,9 @@ abstract class BasePluginTest { fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } fun implementationFiles(vararg paths: Path): String { - return paths.joinToString(lineSeparator) { "implementation files('${it.invariantSeparatorsPathString}')" } + return paths.joinToString(lineSeparator) { + "implementation files('${it.invariantSeparatorsPathString}')" + } } inline fun transform( @@ -430,7 +449,8 @@ abstract class BasePluginTest { $transformerBlock } } - """.trimIndent() + """ + .trimIndent() } fun Assert.useAll(body: Assert.() -> Unit) = all { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt index eee97ff53..e80cb6010 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -30,63 +30,48 @@ class CachingTest : BasePluginTest() { fun dependenciesChanged() { projectScript.appendText( """ - dependencies { - implementation 'my:a:1.0' - implementation 'my:b:1.0' - } - """.trimIndent(), + dependencies { + implementation 'my:a:1.0' + implementation 'my:b:1.0' + } + """ + .trimIndent() ) - assertCompositeExecutions { - containsOnly( - *entriesInAB, - *manifestEntries, - ) - } + assertCompositeExecutions { containsOnly(*entriesInAB, *manifestEntries) } val replaced = projectScript.readText().replace("implementation 'my:b:1.0'", "") projectScript.writeText(replaced) - assertCompositeExecutions { - containsOnly( - *entriesInA, - *manifestEntries, - ) - } + assertCompositeExecutions { containsOnly(*entriesInA, *manifestEntries) } } @Test fun outputFileChanged() { projectScript.appendText( """ - dependencies { - implementation 'my:a:1.0' - implementation 'my:b:1.0' - } - """.trimIndent() + lineSeparator, + dependencies { + implementation 'my:a:1.0' + implementation 'my:b:1.0' + } + """ + .trimIndent() + lineSeparator ) - assertCompositeExecutions { - containsOnly( - *entriesInAB, - *manifestEntries, - ) - } + assertCompositeExecutions { containsOnly(*entriesInAB, *manifestEntries) } projectScript.appendText( """ $shadowJarTask { archiveBaseName = "foo" } - """.trimIndent(), + """ + .trimIndent() ) assertExecutionsFromCacheAndUpToDate() assertThat(jarPath("build/libs/foo-1.0-all.jar")).useAll { - containsOnly( - *entriesInAB, - *manifestEntries, - ) + containsOnly(*entriesInAB, *manifestEntries) } } @@ -94,19 +79,14 @@ class CachingTest : BasePluginTest() { fun dependencyFilterChanged() { projectScript.appendText( """ - dependencies { - implementation 'my:d:1.0' - } - """.trimIndent() + lineSeparator, + dependencies { + implementation 'my:d:1.0' + } + """ + .trimIndent() + lineSeparator ) val assertions = { - assertCompositeExecutions { - containsOnly( - "c.properties", - "d.properties", - *manifestEntries, - ) - } + assertCompositeExecutions { containsOnly("c.properties", "d.properties", *manifestEntries) } } assertions() @@ -116,7 +96,8 @@ class CachingTest : BasePluginTest() { $shadowJarTask { dependencyFilter = new ${MinimizeDependencyFilter::class.java.name}(project) } - """.trimIndent(), + """ + .trimIndent() ) assertions() @@ -124,22 +105,20 @@ class CachingTest : BasePluginTest() { @Test fun duplicatesStrategyChanged() { - listOf( - DuplicatesStrategy.EXCLUDE, - DuplicatesStrategy.INCLUDE, - DuplicatesStrategy.WARN, - ).forEach { strategy -> - projectScript.writeText( - """ + listOf(DuplicatesStrategy.EXCLUDE, DuplicatesStrategy.INCLUDE, DuplicatesStrategy.WARN) + .forEach { strategy -> + projectScript.writeText( + """ ${getDefaultProjectBuildScript()} $shadowJarTask { duplicatesStrategy = DuplicatesStrategy.$strategy } - """.trimIndent(), - ) + """ + .trimIndent() + ) - assertCompositeExecutions() - } + assertCompositeExecutions() + } } @Test @@ -156,7 +135,8 @@ class CachingTest : BasePluginTest() { attributes 'Bar': 'Bar1' } } - """.trimIndent(), + """ + .trimIndent() ) val assertions = { valueFoo: String, valueBar: String -> @@ -178,9 +158,7 @@ class CachingTest : BasePluginTest() { assertions("Foo2", "Bar2") - replaced = projectScript.readText() - .replace("Foo2", "Foo3") - .replace("Bar2", "Bar3") + replaced = projectScript.readText().replace("Foo2", "Foo3").replace("Bar2", "Bar3") projectScript.writeText(replaced) assertions("Foo3", "Bar3") @@ -199,19 +177,16 @@ class CachingTest : BasePluginTest() { it.mainClass.set('$mainClassName') } } - """.trimIndent(), + """ + .trimIndent() ) - assertCompositeExecutions { - getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) - } + assertCompositeExecutions { getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) } val replaced = projectScript.readText().replace(mainClassName, main2ClassName) projectScript.writeText(replaced) - assertCompositeExecutions { - getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) - } + assertCompositeExecutions { getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) } } @Test @@ -225,45 +200,35 @@ class CachingTest : BasePluginTest() { application { mainClass = '$mainClassName' } - """.trimIndent(), + """ + .trimIndent() ) - assertCompositeExecutions { - getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) - } + assertCompositeExecutions { getMainAttr(mainClassAttributeKey).isEqualTo(mainClassName) } val replaced = projectScript.readText().replace(mainClassName, main2ClassName) projectScript.writeText(replaced) - assertCompositeExecutions { - getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) - } + assertCompositeExecutions { getMainAttr(mainClassAttributeKey).isEqualTo(main2ClassName) } } - @Issue( - "https://github.com/GradleUp/shadow/issues/717", - ) + @Issue("https://github.com/GradleUp/shadow/issues/717") @Test fun jarIncludesExcludesChanged() { val mainClassEntry = writeClass(className = "Main") val main2ClassEntry = writeClass(className = "Main2") projectScript.appendText( """ - dependencies { - implementation 'my:a:1.0' - implementation 'my:b:1.0' - } - """.trimIndent() + lineSeparator, + dependencies { + implementation 'my:a:1.0' + implementation 'my:b:1.0' + } + """ + .trimIndent() + lineSeparator ) assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - main2ClassEntry, - *entriesInAB, - *manifestEntries, - ) + containsOnly("my/", mainClassEntry, main2ClassEntry, *entriesInAB, *manifestEntries) } projectScript.appendText( @@ -271,16 +236,12 @@ class CachingTest : BasePluginTest() { $shadowJarTask { exclude '**.properties' } - """.trimIndent() + lineSeparator, + """ + .trimIndent() + lineSeparator ) assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - main2ClassEntry, - *manifestEntries, - ) + containsOnly("my/", mainClassEntry, main2ClassEntry, *manifestEntries) } projectScript.appendText( @@ -288,32 +249,23 @@ class CachingTest : BasePluginTest() { $shadowJarTask { include '$mainClassEntry' } - """.trimIndent() + lineSeparator, + """ + .trimIndent() + lineSeparator ) - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *manifestEntries, - ) - } + assertCompositeExecutions { containsOnly("my/", mainClassEntry, *manifestEntries) } projectScript.appendText( """ $shadowJarTask { include '$main2ClassEntry' } - """.trimIndent() + lineSeparator, + """ + .trimIndent() + lineSeparator ) assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - main2ClassEntry, - *manifestEntries, - ) + containsOnly("my/", mainClassEntry, main2ClassEntry, *manifestEntries) } } @@ -322,19 +274,15 @@ class CachingTest : BasePluginTest() { val mainClassEntry = writeClass(withImports = true) projectScript.appendText( """ - dependencies { - implementation 'junit:junit:3.8.2' - } - """.trimIndent() + lineSeparator, + dependencies { + implementation 'junit:junit:3.8.2' + } + """ + .trimIndent() + lineSeparator ) assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) + containsOnly("my/", mainClassEntry, *junitEntries, *manifestEntries) } projectScript.appendText( @@ -344,16 +292,11 @@ class CachingTest : BasePluginTest() { exclude(dependency('junit:junit')) } } - """.trimIndent(), + """ + .trimIndent() ) - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *manifestEntries, - ) - } + assertCompositeExecutions { containsOnly("my/", mainClassEntry, *manifestEntries) } } @Test @@ -361,16 +304,16 @@ class CachingTest : BasePluginTest() { taskPath = serverShadowJarPath writeClientAndServerModules() - path("server/src/main/java/server/Server.java").writeText( - """ + path("server/src/main/java/server/Server.java") + .writeText( + """ package server; public class Server {} - """.trimIndent(), - ) + """ + .trimIndent() + ) - assertCompositeExecutions( - jarPathProvider = { outputServerShadowedJar }, - ) { + assertCompositeExecutions(jarPathProvider = { outputServerShadowedJar }) { containsOnly( "client/", "server/", @@ -381,25 +324,20 @@ class CachingTest : BasePluginTest() { ) } - path("server/build.gradle").appendText( - """ + path("server/build.gradle") + .appendText( + """ $shadowJarTask { minimize { exclude(dependency('junit:junit:.*')) } } - """.trimIndent(), - ) - - assertCompositeExecutions( - jarPathProvider = { outputServerShadowedJar }, - ) { - containsOnly( - "server/", - "server/Server.class", - *junitEntries, - *manifestEntries, + """ + .trimIndent() ) + + assertCompositeExecutions(jarPathProvider = { outputServerShadowedJar }) { + containsOnly("server/", "server/Server.class", *junitEntries, *manifestEntries) } } @@ -407,20 +345,16 @@ class CachingTest : BasePluginTest() { fun relocatorChanged() { projectScript.appendText( """ - dependencies { - implementation 'junit:junit:3.8.2' - } - """.trimIndent() + lineSeparator, + dependencies { + implementation 'junit:junit:3.8.2' + } + """ + .trimIndent() + lineSeparator ) val mainClassEntry = writeClass(withImports = true) assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) + containsOnly("my/", mainClassEntry, *junitEntries, *manifestEntries) } projectScript.appendText( @@ -428,20 +362,14 @@ class CachingTest : BasePluginTest() { $shadowJarTask { relocate 'junit.framework', 'foo.junit.framework' } - """.trimIndent(), + """ + .trimIndent() ) - val relocatedEntries = junitEntries - .map { it.replace("junit/framework/", "foo/junit/framework/") }.toTypedArray() + val relocatedEntries = + junitEntries.map { it.replace("junit/framework/", "foo/junit/framework/") }.toTypedArray() assertCompositeExecutions { - containsOnly( - "my/", - "foo/", - "foo/junit/", - mainClassEntry, - *relocatedEntries, - *manifestEntries, - ) + containsOnly("my/", "foo/", "foo/junit/", mainClassEntry, *relocatedEntries, *manifestEntries) } } @@ -449,23 +377,19 @@ class CachingTest : BasePluginTest() { fun serviceFileTransformerPropsChanged() { val mainClassEntry = writeClass() val assertions = { - assertCompositeExecutions { - containsOnly( - "my/", - mainClassEntry, - *manifestEntries, - ) - } + assertCompositeExecutions { containsOnly("my/", mainClassEntry, *manifestEntries) } } assertions() projectScript.appendText( transform( - transformerBlock = """ + transformerBlock = + """ path = 'META-INF/foo' - """.trimIndent(), - ), + """ + .trimIndent() + ) ) assertions() @@ -483,7 +407,8 @@ class CachingTest : BasePluginTest() { $shadowJarTask { mergeServiceFiles() } - """.trimIndent() + lineSeparator, + """ + .trimIndent() + lineSeparator ) assertCompositeExecutions() @@ -493,7 +418,8 @@ class CachingTest : BasePluginTest() { $shadowJarTask { mergeGroovyExtensionModules() } - """.trimIndent() + lineSeparator, + """ + .trimIndent() + lineSeparator ) assertCompositeExecutions() @@ -504,7 +430,8 @@ class CachingTest : BasePluginTest() { // Use Transformer.Companion (no-op) to mock a custom transformer here, it's not cacheable. transform(${ResourceTransformer.Companion::class.java.name}) } - """.trimIndent(), + """ + .trimIndent() ) assertExecutionSuccess() @@ -538,7 +465,8 @@ class CachingTest : BasePluginTest() { } /** - * Combines [assertExecutionSuccess] and [assertExecutionsFromCacheAndUpToDate] for simplifying assertions. + * Combines [assertExecutionSuccess] and [assertExecutionsFromCacheAndUpToDate] for simplifying + * assertions. */ private fun assertCompositeExecutions( jarPathProvider: () -> JarPath = { outputShadowedJar }, diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt index 612522f68..be80e5e49 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FilteringTest.kt @@ -16,11 +16,12 @@ class FilteringTest : BasePluginTest() { super.beforeEach() projectScript.appendText( """ - dependencies { - implementation 'my:a:1.0' - implementation 'my:b:1.0' - } - """.trimIndent() + lineSeparator, + dependencies { + implementation 'my:a:1.0' + implementation 'my:b:1.0' + } + """ + .trimIndent() + lineSeparator ) } @@ -28,12 +29,7 @@ class FilteringTest : BasePluginTest() { fun includeAllDependencies() { runWithSuccess(shadowJarPath) - assertThat(outputShadowedJar).useAll { - containsOnly( - *entriesInAB, - *manifestEntries, - ) - } + assertThat(outputShadowedJar).useAll { containsOnly(*entriesInAB, *manifestEntries) } } @Test @@ -43,17 +39,14 @@ class FilteringTest : BasePluginTest() { $shadowJarTask { exclude 'a2.properties' } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "a.properties", - "b.properties", - *manifestEntries, - ) + containsOnly("a.properties", "b.properties", *manifestEntries) } } @@ -62,12 +55,13 @@ class FilteringTest : BasePluginTest() { fun excludeDependency(useAccessor: Boolean) { settingsScript.appendText( """ - dependencyResolutionManagement { - versionCatalogs.create('libs') { - library('my-d', 'my:d:1.0') - } + dependencyResolutionManagement { + versionCatalogs.create('libs') { + library('my-d', 'my:d:1.0') } - """.trimIndent(), + } + """ + .trimIndent() ) val dependency = if (useAccessor) "libs.my.d" else "'my:d:1.0'" projectScript.appendText( @@ -80,7 +74,8 @@ class FilteringTest : BasePluginTest() { exclude(dependency($dependency)) } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -89,16 +84,7 @@ class FilteringTest : BasePluginTest() { } @ParameterizedTest - @ValueSource( - strings = [ - "my:d", - "m.*:d", - "my:d:.*", - "m.*:d:.*", - "m.*:d.*:.*", - ".*:d:.*", - ], - ) + @ValueSource(strings = ["my:d", "m.*:d", "my:d:.*", "m.*:d:.*", "m.*:d.*:.*", ".*:d:.*"]) fun excludeDependencyUsingWildcardSyntax(wildcard: String) { projectScript.appendText( """ @@ -110,7 +96,8 @@ class FilteringTest : BasePluginTest() { exclude(dependency('$wildcard')) } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -130,24 +117,22 @@ class FilteringTest : BasePluginTest() { include(dependency('my:d:1.0')) } } - """.trimIndent(), - ) - path("src/main/java/my/Passed.java").writeText( """ + .trimIndent() + ) + path("src/main/java/my/Passed.java") + .writeText( + """ package my; public class Passed {} - """.trimIndent(), - ) + """ + .trimIndent() + ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "d.properties", - "my/", - "my/Passed.class", - *manifestEntries, - ) + containsOnly("d.properties", "my/", "my/Passed.class", *manifestEntries) } } @@ -156,59 +141,53 @@ class FilteringTest : BasePluginTest() { fun filterProjectDependencies(useAccessor: Boolean) { val clientProject = if (useAccessor) "project(projects.client)" else "project(':client')" writeClientAndServerModules( - serverShadowBlock = """ + serverShadowBlock = + """ dependencies { exclude($clientProject) } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { - containsOnly( - "server/", - "server/Server.class", - *junitEntries, - *manifestEntries, - ) + containsOnly("server/", "server/Server.class", *junitEntries, *manifestEntries) } } - @Issue( - "https://github.com/GradleUp/shadow/issues/671", - ) + @Issue("https://github.com/GradleUp/shadow/issues/671") @Test fun filterProjectThatVersionContainsPlus() { writeClientAndServerModules( - serverShadowBlock = """ + serverShadowBlock = + """ dependencies { exclude(project(':client')) } - """.trimIndent(), + """ + .trimIndent() ) path("client/build.gradle").appendText("version = '1.0.0+1'") runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { - containsOnly( - "server/", - "server/Server.class", - *junitEntries, - *manifestEntries, - ) + containsOnly("server/", "server/Server.class", *junitEntries, *manifestEntries) } } @Test fun excludeTransitiveProjectDependency() { writeClientAndServerModules( - serverShadowBlock = """ + serverShadowBlock = + """ dependencies { exclude { it.moduleGroup == 'junit' } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(serverShadowJarPath) @@ -233,17 +212,14 @@ class FilteringTest : BasePluginTest() { include '*.properties' exclude 'a2.properties' } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "a.properties", - "b.properties", - *manifestEntries, - ) + containsOnly("a.properties", "b.properties", *manifestEntries) } } @@ -260,27 +236,20 @@ class FilteringTest : BasePluginTest() { exclude(dependency($dependency)) } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "f.properties", - *entriesInAB, - *manifestEntries, - ) + containsOnly("f.properties", *entriesInAB, *manifestEntries) } } private fun commonAssertions() { assertThat(outputShadowedJar).useAll { - containsOnly( - "c.properties", - *entriesInAB, - *manifestEntries, - ) + containsOnly("c.properties", *entriesInAB, *manifestEntries) } } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt index 115d940c1..e2c1edffc 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/FindResourceInClasspathTest.kt @@ -29,16 +29,18 @@ class FindResourceInClasspathTest : BasePluginTest() { classpath = configurations.runtimeClasspath exclude("a.properties") } - """.trimIndent(), + """ + .trimIndent() ) - assertThat(runWithSuccess(":find1").output).contains( - "> Task :find1", - "scanning ", - "/my/a/1.0/a-1.0.jar".variantSeparatorsPathString, - "/a.properties".variantSeparatorsPathString, - "/a2.properties".variantSeparatorsPathString, - ) + assertThat(runWithSuccess(":find1").output) + .contains( + "> Task :find1", + "scanning ", + "/my/a/1.0/a-1.0.jar".variantSeparatorsPathString, + "/a.properties".variantSeparatorsPathString, + "/a2.properties".variantSeparatorsPathString, + ) assertThat(runWithSuccess(":find2").output).all { contains( @@ -47,9 +49,7 @@ class FindResourceInClasspathTest : BasePluginTest() { "/my/a/1.0/a-1.0.jar".variantSeparatorsPathString, "/a.properties".variantSeparatorsPathString, ) - doesNotContain( - "/a2.properties".variantSeparatorsPathString, - ) + doesNotContain("/a2.properties".variantSeparatorsPathString) } assertThat(runWithSuccess(":find3").output).all { @@ -59,9 +59,7 @@ class FindResourceInClasspathTest : BasePluginTest() { "/my/a/1.0/a-1.0.jar".variantSeparatorsPathString, "/a2.properties".variantSeparatorsPathString, ) - doesNotContain( - "/a.properties".variantSeparatorsPathString, - ) + doesNotContain("/a.properties".variantSeparatorsPathString) } } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 767325988..b1016fa16 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -47,13 +47,11 @@ import org.junit.jupiter.params.provider.MethodSource import org.junit.jupiter.params.provider.ValueSource class JavaPluginsTest : BasePluginTest() { - @Issue( - "https://github.com/GradleUp/shadow/pull/1766", - ) + @Issue("https://github.com/GradleUp/shadow/pull/1766") @Test fun makeAssembleDependOnShadowJarEvenIfAddedLater() { - val kFunction = ShadowJar.Companion::class.declaredFunctions - .single { it.name == "registerShadowJarCommon" } + val kFunction = + ShadowJar.Companion::class.declaredFunctions.single { it.name == "registerShadowJarCommon" } val jvmName = checkNotNull(kFunction.javaMethod).name projectScript.writeText( @@ -77,38 +75,39 @@ class JavaPluginsTest : BasePluginTest() { } } } - """.trimIndent(), + """ + .trimIndent() ) val result = runWithSuccess(ASSEMBLE_TASK_NAME) - assertThat(result.task(":$ASSEMBLE_TASK_NAME")).isNotNull() - .transform { it.outcome }.isEqualTo(SUCCESS) - assertThat(result.task(shadowJarPath)).isNotNull() - .transform { it.outcome }.isEqualTo(SUCCESS) - assertThat(result.output).contains( - "task dependencies: $SHADOW_JAR_TASK_NAME", - ) + assertThat(result.task(":$ASSEMBLE_TASK_NAME")) + .isNotNull() + .transform { it.outcome } + .isEqualTo(SUCCESS) + assertThat(result.task(shadowJarPath)).isNotNull().transform { it.outcome }.isEqualTo(SUCCESS) + assertThat(result.output).contains("task dependencies: $SHADOW_JAR_TASK_NAME") } @Test fun shadowJarCliOptions() { val result = runWithSuccess("help", "--task", shadowJarPath) - assertThat(result.output).contains( - "--add-multi-release-attribute Adds the multi-release attribute to the manifest if any dependencies contain it.", - "--no-add-multi-release-attribute Disables option --add-multi-release-attribute.", - "--enable-auto-relocation Enables auto relocation of packages in the dependencies.", - "--no-enable-auto-relocation Disables option --enable-auto-relocation.", - "--enable-kotlin-module-remapping Enables remapping of Kotlin module metadata files.", - "--no-enable-kotlin-module-remapping Disables option --enable-kotlin-module-remapping.", - "--fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate.", - "--no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries", - "--main-class Main class attribute to add to manifest.", - "--minimize-jar Minimizes the jar by removing unused classes.", - "--no-minimize-jar Disables option --minimize-jar.", - "--relocation-prefix Prefix used for auto relocation of packages in the dependencies.", - ) + assertThat(result.output) + .contains( + "--add-multi-release-attribute Adds the multi-release attribute to the manifest if any dependencies contain it.", + "--no-add-multi-release-attribute Disables option --add-multi-release-attribute.", + "--enable-auto-relocation Enables auto relocation of packages in the dependencies.", + "--no-enable-auto-relocation Disables option --enable-auto-relocation.", + "--enable-kotlin-module-remapping Enables remapping of Kotlin module metadata files.", + "--no-enable-kotlin-module-remapping Disables option --enable-kotlin-module-remapping.", + "--fail-on-duplicate-entries Fails build if the ZIP entries in the shadowed JAR are duplicate.", + "--no-fail-on-duplicate-entries Disables option --fail-on-duplicate-entries", + "--main-class Main class attribute to add to manifest.", + "--minimize-jar Minimizes the jar by removing unused classes.", + "--no-minimize-jar Disables option --minimize-jar.", + "--relocation-prefix Prefix used for auto relocation of packages in the dependencies.", + ) } @Test @@ -136,29 +135,19 @@ class JavaPluginsTest : BasePluginTest() { runWithSuccess(":server:jar") assertThat(jarPath("server/build/libs/server-1.0.jar")).useAll { - containsOnly( - "server/", - "server/Server.class", - *manifestEntries, - ) + containsOnly("server/", "server/Server.class", *manifestEntries) } assertThat(jarPath("client/build/libs/client-1.0-all.jar")).useAll { - containsAtLeast( - "client/", - "client/Client.class", - "client/junit/framework/Test.class", - ) - containsNone( - "server/Server.class", - ) + containsAtLeast("client/", "client/Client.class", "client/junit/framework/Test.class") + containsNone("server/Server.class") } } @Test fun shadowProjectShadowJar() { writeClientAndServerModules(clientShadowed = true) - val relocatedEntries = junitEntries - .map { it.replace("junit/framework/", "client/junit/framework/") }.toTypedArray() + val relocatedEntries = + junitEntries.map { it.replace("junit/framework/", "client/junit/framework/") }.toTypedArray() runWithSuccess(serverShadowJarPath) @@ -174,44 +163,43 @@ class JavaPluginsTest : BasePluginTest() { ) } assertThat(jarPath("client/build/libs/client-1.0-all.jar")).useAll { - containsAtLeast( - "client/Client.class", - "client/junit/framework/Test.class", - ) - containsNone( - "server/Server.class", - ) + containsAtLeast("client/Client.class", "client/junit/framework/Test.class") + containsNone("server/Server.class") } } - @Issue( - "https://github.com/GradleUp/shadow/issues/1606", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1606") @Test fun shadowExposedCustomSourceSetOutput() { writeClientAndServerModules() - path("client/build.gradle").appendText( - """ + path("client/build.gradle") + .appendText( + """ sourceSets { custom } dependencies { implementation sourceSets.custom.output } - """.trimIndent(), - ) - path("client/src/custom/java/client/Custom1.java").writeText( - """ + """ + .trimIndent() + ) + path("client/src/custom/java/client/Custom1.java") + .writeText( + """ package client; public class Custom1 {} - """.trimIndent(), - ) - path("client/src/custom/java/client/Custom2.java").writeText( - """ + """ + .trimIndent() + ) + path("client/src/custom/java/client/Custom2.java") + .writeText( + """ package client; public class Custom2 {} - """.trimIndent(), - ) + """ + .trimIndent() + ) path("client/src/custom/resources/Foo.bar").writeText("Foo=Bar") runWithSuccess(serverShadowJarPath) @@ -231,39 +219,42 @@ class JavaPluginsTest : BasePluginTest() { } } - @Issue( - "https://github.com/GradleUp/shadow/issues/449", - ) + @Issue("https://github.com/GradleUp/shadow/issues/449") @ParameterizedTest @ValueSource(booleans = [false, true]) fun containsMultiReleaseAttrIfAnyDependencyContainsIt(addAttribute: Boolean) { writeClientAndServerModules() - path("client/build.gradle").appendText( - """ + path("client/build.gradle") + .appendText( + """ $jarTask { manifest { attributes '$multiReleaseAttributeKey': 'true' } } - """.trimIndent() + lineSeparator, - ) - path("server/build.gradle").appendText( """ + .trimIndent() + lineSeparator + ) + path("server/build.gradle") + .appendText( + """ $shadowJarTask { addMultiReleaseAttribute = $addAttribute } - """.trimIndent(), - ) + """ + .trimIndent() + ) val result = runWithSuccess(serverShadowJarPath, infoArgument) - assertThat(result.output).contains( - if (addAttribute) { - "Adding Multi-Release attribute to the manifest if any dependencies contain it." - } else { - "Skipping adding Multi-Release attribute to the manifest as it is disabled." - }, - ) + assertThat(result.output) + .contains( + if (addAttribute) { + "Adding Multi-Release attribute to the manifest if any dependencies contain it." + } else { + "Skipping adding Multi-Release attribute to the manifest as it is disabled." + } + ) assertThat(outputServerShadowedJar.use { it.getMainAttr(multiReleaseAttributeKey) }) .isEqualTo(if (addAttribute) "true" else null) } @@ -272,26 +263,29 @@ class JavaPluginsTest : BasePluginTest() { @ValueSource(booleans = [false, true]) fun containsMultiReleaseAttrByCliOption(enable: Boolean) { writeClientAndServerModules() - path("client/build.gradle").appendText( - """ + path("client/build.gradle") + .appendText( + """ $jarTask { manifest { attributes '$multiReleaseAttributeKey': 'true' } } - """.trimIndent() + lineSeparator, - ) + """ + .trimIndent() + lineSeparator + ) val arg = if (enable) "--add-multi-release-attribute" else "--no-add-multi-release-attribute" val result = runWithSuccess(serverShadowJarPath, infoArgument, arg) - assertThat(result.output).contains( - if (enable) { - "Adding Multi-Release attribute to the manifest if any dependencies contain it." - } else { - "Skipping adding Multi-Release attribute to the manifest as it is disabled." - }, - ) + assertThat(result.output) + .contains( + if (enable) { + "Adding Multi-Release attribute to the manifest if any dependencies contain it." + } else { + "Skipping adding Multi-Release attribute to the manifest as it is disabled." + } + ) assertThat(outputServerShadowedJar.use { it.getMainAttr(multiReleaseAttributeKey) }) .isEqualTo(if (enable) "true" else null) } @@ -302,77 +296,64 @@ class JavaPluginsTest : BasePluginTest() { ) @Test fun excludeSomeResourcesByDefault() { - val resJar = buildJar("meta-inf.jar") { - insert("META-INF/INDEX.LIST", "JarIndex-Version: 1.0") - insert("META-INF/a.SF", "Signature File") - insert("META-INF/a.DSA", "DSA Signature Block") - insert("META-INF/a.RSA", "RSA Signature Block") - insert("META-INF/a.properties", "key=value") - insert("META-INF/versions/9/module-info.class", "module myModuleName {}") - insert("META-INF/versions/16/module-info.class", "module myModuleName {}") - insert("module-info.class", "module myModuleName {}") - } + val resJar = + buildJar("meta-inf.jar") { + insert("META-INF/INDEX.LIST", "JarIndex-Version: 1.0") + insert("META-INF/a.SF", "Signature File") + insert("META-INF/a.DSA", "DSA Signature Block") + insert("META-INF/a.RSA", "RSA Signature Block") + insert("META-INF/a.properties", "key=value") + insert("META-INF/versions/9/module-info.class", "module myModuleName {}") + insert("META-INF/versions/16/module-info.class", "module myModuleName {}") + insert("module-info.class", "module myModuleName {}") + } projectScript.appendText( """ dependencies { ${implementationFiles(resJar)} } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) - assertThat(outputShadowedJar).useAll { - containsOnly( - "META-INF/a.properties", - *manifestEntries, - ) - } + assertThat(outputShadowedJar).useAll { containsOnly("META-INF/a.properties", *manifestEntries) } } @Test fun includeRuntimeConfigurationByDefault() { projectScript.appendText( """ - dependencies { - runtimeOnly 'my:a:1.0' - shadow 'my:b:1.0' - compileOnly 'my:b:1.0' - } - """.trimIndent(), + dependencies { + runtimeOnly 'my:a:1.0' + shadow 'my:b:1.0' + compileOnly 'my:b:1.0' + } + """ + .trimIndent() ) runWithSuccess(shadowJarPath) - assertThat(outputShadowedJar).useAll { - containsOnly( - *entriesInA, - *manifestEntries, - ) - } + assertThat(outputShadowedJar).useAll { containsOnly(*entriesInA, *manifestEntries) } } @Test fun includeJavaLibraryConfigurationsByDefault() { - localRepo.apply { - jarModule("my", "api", "1.0") { - buildJar { - insert("api.properties", "api") - } - } - jarModule("my", "implementation", "1.0") { - buildJar { - insert("implementation.properties", "implementation") + localRepo + .apply { + jarModule("my", "api", "1.0") { buildJar { insert("api.properties", "api") } } + jarModule("my", "implementation", "1.0") { + buildJar { insert("implementation.properties", "implementation") } + addDependency("my:b:1.0") } - addDependency("my:b:1.0") - } - jarModule("my", "runtime-only", "1.0") { - buildJar { - insert("runtime-only.properties", "runtime-only") + jarModule("my", "runtime-only", "1.0") { + buildJar { insert("runtime-only.properties", "runtime-only") } } } - }.publish() + .publish() projectScript.writeText( """ @@ -382,7 +363,8 @@ class JavaPluginsTest : BasePluginTest() { implementation 'my:implementation:1.0' runtimeOnly 'my:runtime-only:1.0' } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -402,10 +384,11 @@ class JavaPluginsTest : BasePluginTest() { fun classPathInManifestNotAddedIfEmpty() { projectScript.appendText( """ - dependencies { - implementation 'junit:junit:3.8.2' - } - """.trimIndent(), + dependencies { + implementation 'junit:junit:3.8.2' + } + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -416,9 +399,7 @@ class JavaPluginsTest : BasePluginTest() { } } - @Issue( - "https://github.com/GradleUp/shadow/issues/65", - ) + @Issue("https://github.com/GradleUp/shadow/issues/65") @ParameterizedTest @ValueSource(strings = [ShadowBasePlugin.CONFIGURATION_NAME, IMPLEMENTATION_CONFIGURATION_NAME]) fun addShadowConfigurationToClassPathInManifest(configuration: String) { @@ -432,30 +413,31 @@ class JavaPluginsTest : BasePluginTest() { attributes '$classPathAttributeKey': '/libs/foo.jar' } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) val actual = outputShadowedJar.use { it.getMainAttr(classPathAttributeKey) } - val expected = when (configuration) { - ShadowBasePlugin.CONFIGURATION_NAME -> "/libs/foo.jar junit-3.8.2.jar" - else -> "/libs/foo.jar" - } + val expected = + when (configuration) { + ShadowBasePlugin.CONFIGURATION_NAME -> "/libs/foo.jar junit-3.8.2.jar" + else -> "/libs/foo.jar" + } assertThat(actual).isEqualTo(expected) } - @Issue( - "https://github.com/GradleUp/shadow/issues/92", - ) + @Issue("https://github.com/GradleUp/shadow/issues/92") @Test fun doNotIncludeNullValueInClassPathWhenJarFileDoesNotContainClassPath() { projectScript.appendText( """ - dependencies { - shadow 'junit:junit:3.8.2' - } - """.trimIndent(), + dependencies { + shadow 'junit:junit:3.8.2' + } + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -464,9 +446,7 @@ class JavaPluginsTest : BasePluginTest() { assertThat(value).isEqualTo("junit-3.8.2.jar") } - @Issue( - "https://github.com/GradleUp/shadow/issues/203", - ) + @Issue("https://github.com/GradleUp/shadow/issues/203") @ParameterizedTest @EnumSource(ZipEntryCompression::class) fun supportZipCompressions(method: ZipEntryCompression) { @@ -479,17 +459,13 @@ class JavaPluginsTest : BasePluginTest() { zip64 = true entryCompression = ${ZipEntryCompression::class.java.canonicalName}.$method } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) - assertThat(outputShadowedJar).useAll { - containsOnly( - *junitEntries, - *manifestEntries, - ) - } + assertThat(outputShadowedJar).useAll { containsOnly(*junitEntries, *manifestEntries) } } @Issue( @@ -501,18 +477,22 @@ class JavaPluginsTest : BasePluginTest() { writeGradlePluginModule() projectScript.appendText( """ - dependencies { - implementation 'my:a:1.0' - compileOnly 'my:b:1.0' - } - """.trimIndent(), + dependencies { + implementation 'my:a:1.0' + compileOnly 'my:b:1.0' + } + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - transform { actual -> actual.entries().toList().map { it.name }.filter { it.endsWith(".class") } } - .single().isEqualTo("my/plugin/MyPlugin.class") + transform { actual -> + actual.entries().toList().map { it.name }.filter { it.endsWith(".class") } + } + .single() + .isEqualTo("my/plugin/MyPlugin.class") transform { it.mainAttrSize }.isEqualTo(1) // Doesn't contain Gradle classes. getMainAttr(classPathAttributeKey).isNull() @@ -529,9 +509,7 @@ class JavaPluginsTest : BasePluginTest() { } } - @Issue( - "https://github.com/GradleUp/shadow/issues/1422", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1422") @Test fun moveLocalGradleApiToCompileOnly() { projectScript.writeText(getDefaultProjectBuildScript("java-gradle-plugin")) @@ -544,27 +522,24 @@ class JavaPluginsTest : BasePluginTest() { assertThat(outputApi).doesNotContain("unspecified") } - @Issue( - "https://github.com/GradleUp/shadow/issues/1422", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1422") @ParameterizedTest @ValueSource(strings = [COMPILE_ONLY_CONFIGURATION_NAME, API_CONFIGURATION_NAME]) fun doNotReAddSuppressedGradleApi(configuration: String) { projectScript.writeText(getDefaultProjectBuildScript("java-gradle-plugin")) - val output = dependencies( - configuration = configuration, - // Internal flag added in 8.14 to experiment with suppressing local Gradle API. - "-Dorg.gradle.unsafe.suppress-gradle-api=true", - ) + val output = + dependencies( + configuration = configuration, + // Internal flag added in 8.14 to experiment with suppressing local Gradle API. + "-Dorg.gradle.unsafe.suppress-gradle-api=true", + ) // "unspecified" is the local Gradle API. assertThat(output).doesNotContain("unspecified") } - @Issue( - "https://github.com/GradleUp/shadow/issues/1070", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1070") @Test fun registerCustomShadowJarTask() { val mainClassEntry = writeClass(sourceSet = "test", withImports = true) @@ -583,32 +558,24 @@ class JavaPluginsTest : BasePluginTest() { attributes '$mainClassAttributeKey': 'my.Main' } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(testShadowJarTask) assertThat(jarPath("build/libs/my-1.0-test.jar")).useAll { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) + containsOnly("my/", mainClassEntry, *junitEntries, *manifestEntries) getMainAttr(mainClassAttributeKey).isNotNull() } val pathString = path("build/libs/my-1.0-test.jar").toString() val runningOutput = runProcess("java", "-jar", pathString, "foo") - assertThat(runningOutput).contains( - "Hello, World! (foo) from Main", - "Refs: junit.framework.Test", - ) + assertThat(runningOutput) + .contains("Hello, World! (foo) from Main", "Refs: junit.framework.Test") } - @Issue( - "https://github.com/GradleUp/shadow/issues/1784", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1784") @Test fun registerShadowJarTaskWithoutShadowPluginApplied() { val mainClassEntry = writeClass(sourceSet = "test", withImports = true) @@ -634,37 +601,26 @@ class JavaPluginsTest : BasePluginTest() { logger.lifecycle("Has ShadowPlugin: " + hasShadowPlugin) logger.lifecycle("Has ShadowBasePlugin: " + hasShadowBasePlugin) } - """.trimIndent(), + """ + .trimIndent() ) val result = runWithSuccess(testShadowJarTask) - assertThat(result.output).contains( - "Has ShadowPlugin: false", - "Has ShadowBasePlugin: false", - ) + assertThat(result.output).contains("Has ShadowPlugin: false", "Has ShadowBasePlugin: false") assertThat(jarPath("build/libs/my-1.0-test.jar")).useAll { - containsOnly( - "my/", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) + containsOnly("my/", mainClassEntry, *junitEntries, *manifestEntries) getMainAttr(mainClassAttributeKey).isNotNull() } val pathString = path("build/libs/my-1.0-test.jar").toString() val runningOutput = runProcess("java", "-jar", pathString, "foo") - assertThat(runningOutput).contains( - "Hello, World! (foo) from Main", - "Refs: junit.framework.Test", - ) + assertThat(runningOutput) + .contains("Hello, World! (foo) from Main", "Refs: junit.framework.Test") } - @Issue( - "https://github.com/GradleUp/shadow/issues/443", - ) + @Issue("https://github.com/GradleUp/shadow/issues/443") @Test fun registerCustomShadowJarThatContainsDependenciesOnly() { val mainClassEntry = writeClass() @@ -680,43 +636,34 @@ class JavaPluginsTest : BasePluginTest() { archiveClassifier = 'dep' configurations = project.configurations.named('runtimeClasspath').map { [it] } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess("jar", dependencyShadowJar) assertThat(jarPath("build/libs/my-1.0.jar")).useAll { - containsOnly( - "my/", - mainClassEntry, - *manifestEntries, - ) + containsOnly("my/", mainClassEntry, *manifestEntries) transform { it.mainAttrSize }.isEqualTo(1) } assertThat(jarPath("build/libs/my-1.0-dep.jar")).useAll { - containsOnly( - *junitEntries, - *manifestEntries, - ) + containsOnly(*junitEntries, *manifestEntries) transform { it.mainAttrSize }.isEqualTo(1) } } - @Issue( - "https://github.com/GradleUp/shadow/issues/915", - ) + @Issue("https://github.com/GradleUp/shadow/issues/915") @Test fun failBuildIfProcessingBadJar() { - val badJarPath = path("bad.jar").apply { - writeText("A bad jar.") - } + val badJarPath = path("bad.jar").apply { writeText("A bad jar.") } projectScript.appendText( """ dependencies { ${implementationFiles(badJarPath)} } - """.trimIndent(), + """ + .trimIndent() ) val result = runWithFailure(shadowJarPath) @@ -733,15 +680,14 @@ class JavaPluginsTest : BasePluginTest() { dependencies { ${implementationFiles(fooAarPath)} } - """.trimIndent(), + """ + .trimIndent() ) val result = runWithFailure(shadowJarPath) - assertThat(result.output).contains( - "Shadowing AAR file is not supported.", - "Please exclude dependency artifact:", - ) + assertThat(result.output) + .contains("Shadowing AAR file is not supported.", "Please exclude dependency artifact:") } @Test @@ -761,7 +707,8 @@ class JavaPluginsTest : BasePluginTest() { into('Bar') } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -785,9 +732,7 @@ class JavaPluginsTest : BasePluginTest() { inputStream.copyTo(unzipped.outputStream()) } } - assertThat(jarPath(unzipped.name)).useAll { - containsOnly(*entriesInA) - } + assertThat(jarPath(unzipped.name)).useAll { containsOnly(*entriesInA) } } @Test @@ -802,32 +747,23 @@ class JavaPluginsTest : BasePluginTest() { $shadowJarTask { from(nonJar) } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "a-1.0.jar", - "b-1.0.jar", - *manifestEntries, - ) + containsOnly("a-1.0.jar", "b-1.0.jar", *manifestEntries) } } - @Issue( - "https://github.com/GradleUp/shadow/issues/520", - ) + @Issue("https://github.com/GradleUp/shadow/issues/520") @Test fun onlyKeepFilesFromProjectWhenDuplicatesStrategyIsExclude() { - val fooJar = buildJar("foo.jar") { - insert("module-info.class", "module myModuleName {}") - } + val fooJar = buildJar("foo.jar") { insert("module-info.class", "module myModuleName {}") } val mainClassEntry = writeClass() - writeClass(className = "module-info") { - "module myModuleName {}" - } + writeClass(className = "module-info") { "module myModuleName {}" } projectScript.appendText( """ dependencies { @@ -838,18 +774,14 @@ class JavaPluginsTest : BasePluginTest() { 'module-info.class' ) } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "module-info.class", - "my/", - mainClassEntry, - *manifestEntries, - ) + containsOnly("module-info.class", "my/", mainClassEntry, *manifestEntries) getContent("module-info.class").all { isNotEmpty() // It's the compiled class instead of the original content. @@ -858,9 +790,7 @@ class JavaPluginsTest : BasePluginTest() { } } - @Issue( - "https://github.com/GradleUp/shadow/issues/1441", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1441") @Test fun includeFilesInTaskOutputDirectory() { // Create a build that has a task with jars in the output directory @@ -888,14 +818,13 @@ class JavaPluginsTest : BasePluginTest() { $$shadowJarTask { includedDependencies.from(files(createJars).asFileTree) } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) - assertThat(outputShadowedJar).useAll { - containsOnly(*entriesInAB, *manifestEntries) - } + assertThat(outputShadowedJar).useAll { containsOnly(*entriesInAB, *manifestEntries) } } @Test @@ -903,25 +832,25 @@ class JavaPluginsTest : BasePluginTest() { writeClientAndServerModules() settingsScript.prependText( """ - plugins { - id 'com.gradle.develocity' - } - """.trimIndent() + lineSeparator, + plugins { + id 'com.gradle.develocity' + } + """ + .trimIndent() + lineSeparator ) - val result = runWithSuccess( - serverShadowJarPath, - ipArgument, - infoArgument, - "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", - "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a file instead. - ) + val result = + runWithSuccess( + serverShadowJarPath, + ipArgument, + infoArgument, + "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", + "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a + // file instead. + ) assertThat(result.output).all { - contains( - "Enabling Develocity integration for Shadow plugin.", - "Build scan written", - ) + contains("Enabling Develocity integration for Shadow plugin.", "Build scan written") doesNotContain("Configuration cache problems") } } @@ -939,19 +868,19 @@ class JavaPluginsTest : BasePluginTest() { duplicatesStrategy = DuplicatesStrategy.INCLUDE failOnDuplicateEntries = $enable } - """.trimIndent(), + """ + .trimIndent() ) - val result = if (enable) { - runWithFailure(shadowJarPath) - } else { - runWithSuccess(shadowJarPath, infoArgument) - } + val result = + if (enable) { + runWithFailure(shadowJarPath) + } else { + runWithSuccess(shadowJarPath, infoArgument) + } - assertThat(result.output).contains( - "Duplicate entries found in the shadowed JAR:", - "a.properties (2 times)", - ) + assertThat(result.output) + .contains("Duplicate entries found in the shadowed JAR:", "a.properties (2 times)") } @ParameterizedTest @@ -966,19 +895,19 @@ class JavaPluginsTest : BasePluginTest() { $shadowJarTask { duplicatesStrategy = DuplicatesStrategy.INCLUDE } - """.trimIndent(), + """ + .trimIndent() ) - val result = if (enable) { - runWithFailure(shadowJarPath, "--fail-on-duplicate-entries") - } else { - runWithSuccess(shadowJarPath, infoArgument, "--no-fail-on-duplicate-entries") - } + val result = + if (enable) { + runWithFailure(shadowJarPath, "--fail-on-duplicate-entries") + } else { + runWithSuccess(shadowJarPath, infoArgument, "--no-fail-on-duplicate-entries") + } - assertThat(result.output).contains( - "Duplicate entries found in the shadowed JAR:", - "a.properties (2 times)", - ) + assertThat(result.output) + .contains("Duplicate entries found in the shadowed JAR:", "a.properties (2 times)") } @ParameterizedTest @@ -989,17 +918,14 @@ class JavaPluginsTest : BasePluginTest() { $shadowJarTask { mainClass = '$input' } - """.trimIndent(), + """ + .trimIndent() ) val result = runWithSuccess(shadowJarPath, infoArgument) - assertThat(result.output).contains( - message, - ) - assertThat(outputShadowedJar).useAll { - getMainAttr(mainClassAttributeKey).isEqualTo(expected) - } + assertThat(result.output).contains(message) + assertThat(outputShadowedJar).useAll { getMainAttr(mainClassAttributeKey).isEqualTo(expected) } } @ParameterizedTest @@ -1011,9 +937,7 @@ class JavaPluginsTest : BasePluginTest() { runWithSuccess(shadowJarPath, "--main-class", input) } - assertThat(outputShadowedJar).useAll { - getMainAttr(mainClassAttributeKey).isEqualTo(expected) - } + assertThat(outputShadowedJar).useAll { getMainAttr(mainClassAttributeKey).isEqualTo(expected) } } private fun dependencies(configuration: String, vararg flags: String): String { @@ -1022,9 +946,18 @@ class JavaPluginsTest : BasePluginTest() { private companion object { @JvmStatic - fun fallbackMainClassProvider() = listOf( - Arguments.of("my.Main", "my.Main", "Adding $mainClassAttributeKey attribute to the manifest with value"), - Arguments.of("", null, "Skipping adding $mainClassAttributeKey attribute to the manifest as it is empty."), - ) + fun fallbackMainClassProvider() = + listOf( + Arguments.of( + "my.Main", + "my.Main", + "Adding $mainClassAttributeKey attribute to the manifest with value", + ), + Arguments.of( + "", + null, + "Skipping adding $mainClassAttributeKey attribute to the manifest as it is empty.", + ), + ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt index 6a13603d1..84b763e4a 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/KotlinPluginsTest.kt @@ -21,7 +21,9 @@ class KotlinPluginsTest : BasePluginTest() { @BeforeEach override fun beforeEach() { super.beforeEach() - projectScript.writeText(getDefaultProjectBuildScript(plugin = "org.jetbrains.kotlin.multiplatform")) + projectScript.writeText( + getDefaultProjectBuildScript(plugin = "org.jetbrains.kotlin.multiplatform") + ) } @ParameterizedTest @@ -36,20 +38,16 @@ class KotlinPluginsTest : BasePluginTest() { implementation 'junit:junit:3.8.2' $stdlib } - """.trimIndent(), + """ + .trimIndent() ) val mainClassEntry = writeClass(withImports = true, jvmLang = JvmLang.Kotlin) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - val entries = arrayOf( - "my/", - "META-INF/my.kotlin_module", - mainClassEntry, - *junitEntries, - *manifestEntries, - ) + val entries = + arrayOf("my/", "META-INF/my.kotlin_module", mainClassEntry, *junitEntries, *manifestEntries) if (excludeStdlib) { containsOnly(*entries) } else { @@ -82,19 +80,15 @@ class KotlinPluginsTest : BasePluginTest() { } } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - val entries = arrayOf( - "my/", - "META-INF/my.kotlin_module", - mainClassEntry, - *entriesInAB, - *manifestEntries, - ) + val entries = + arrayOf("my/", "META-INF/my.kotlin_module", mainClassEntry, *entriesInAB, *manifestEntries) if (excludeStdlib) { containsOnly(*entries) } else { @@ -103,9 +97,7 @@ class KotlinPluginsTest : BasePluginTest() { } } - @Issue( - "https://github.com/GradleUp/shadow/issues/1377", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1377") @Test fun compatKmpForOtherNamedJvmTarget() { val jvmTargetName = "newJvm" @@ -130,41 +122,37 @@ class KotlinPluginsTest : BasePluginTest() { } } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - val entries = arrayOf( - "my/", - "META-INF/my.kotlin_module", - mainClassEntry, - *entriesInAB, - *manifestEntries, - ) + val entries = + arrayOf("my/", "META-INF/my.kotlin_module", mainClassEntry, *entriesInAB, *manifestEntries) containsAtLeast(*entries) } } - @Issue( - "https://github.com/GradleUp/shadow/issues/1377", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1377") @Test fun doNotCreateJvmTargetEagerly() { projectScript.appendText( """ - kotlin { - mingwX64() - } - """.trimIndent(), + kotlin { + mingwX64() + } + """ + .trimIndent() ) val result = runWithFailure(shadowJarPath) - assertThat(result.output).contains( - "Cannot locate tasks that match ':shadowJar' as task 'shadowJar' not found in root project", - ) + assertThat(result.output) + .contains( + "Cannot locate tasks that match ':shadowJar' as task 'shadowJar' not found in root project" + ) } @ParameterizedTest @@ -172,7 +160,8 @@ class KotlinPluginsTest : BasePluginTest() { fun setMainClassAttributeFromMainRun(useShadowAttr: Boolean) { val mainClassName = "my.Main" val main2ClassName = "my.Main2" - val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" + val mainAttr = + if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" projectScript.appendText( """ kotlin { @@ -185,13 +174,15 @@ class KotlinPluginsTest : BasePluginTest() { $mainAttr } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - getMainAttr(mainClassAttributeKey).isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) + getMainAttr(mainClassAttributeKey) + .isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) } } @@ -200,7 +191,8 @@ class KotlinPluginsTest : BasePluginTest() { fun setManifestAttrsFromJvmTargetJar(useShadowAttr: Boolean) { val mainClassName = "my.Main" val main2ClassName = "my.Main2" - val mainAttr = if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" + val mainAttr = + if (useShadowAttr) "attributes '$mainClassAttributeKey': '$main2ClassName'" else "" projectScript.appendText( """ kotlin { @@ -216,13 +208,15 @@ class KotlinPluginsTest : BasePluginTest() { $mainAttr } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - getMainAttr(mainClassAttributeKey).isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) + getMainAttr(mainClassAttributeKey) + .isEqualTo(if (useShadowAttr) main2ClassName else mainClassName) } } @@ -252,15 +246,17 @@ class KotlinPluginsTest : BasePluginTest() { } } } - """.trimIndent(), + """ + .trimIndent() ) val result = runWithFailure(shadowJarPath, infoArgument) - assertThat(result.output).contains( - "$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: $jvmTargetName", // Logged from Shadow. - "Declaring multiple Kotlin Targets of the same type is not supported.", // Thrown from KGP. - ) + assertThat(result.output) + .contains( + "$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: $jvmTargetName", // Logged from Shadow. + "Declaring multiple Kotlin Targets of the same type is not supported.", // Thrown from KGP. + ) } private fun compileOnlyStdlib(exclude: Boolean): String { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt index 272ee76fd..aa50d6e82 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/MinimizeTest.kt @@ -14,12 +14,13 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource class MinimizeTest : BasePluginTest() { - private val outputImplShadowedJar: JarPath get() = jarPath("impl/build/libs/impl-1.0-all.jar") + private val outputImplShadowedJar: JarPath + get() = jarPath("impl/build/libs/impl-1.0-all.jar") /** - * 'api' used as api for 'impl', and depended on 'lib'. 'junit' is independent. - * The minimize step shall remove 'junit', but not 'api'. - * Unused classes of 'api' and theirs dependencies also shouldn't be removed. + * 'api' used as api for 'impl', and depended on 'lib'. 'junit' is independent. The minimize step + * shall remove 'junit', but not 'api'. Unused classes of 'api' and theirs dependencies also + * shouldn't be removed. */ @Test fun useMinimizeWithDependenciesWithApiScope() { @@ -42,22 +43,24 @@ class MinimizeTest : BasePluginTest() { } /** - * 'api' used as api for 'impl', and 'lib' used as api for 'api'. - * Unused classes of 'api' and 'lib' shouldn't be removed. + * 'api' used as api for 'impl', and 'lib' used as api for 'api'. Unused classes of 'api' and + * 'lib' shouldn't be removed. */ @Test fun useMinimizeWithTransitiveDependenciesWithApiScope() { writeApiLibAndImplModules() - path("api/build.gradle").writeText( - """ + path("api/build.gradle") + .writeText( + """ plugins { id 'java-library' } dependencies { api project(':lib') } - """.trimIndent(), - ) + """ + .trimIndent() + ) runWithSuccess(":impl:$SHADOW_JAR_TASK_NAME") @@ -76,84 +79,76 @@ class MinimizeTest : BasePluginTest() { } } - /** - * 'Server' depends on 'Client'. 'junit' is independent. - * The minimize shall remove 'junit'. - */ + /** 'Server' depends on 'Client'. 'junit' is independent. The minimize shall remove 'junit'. */ @Test fun minimizeByKeepingOnlyTransitiveDependencies() { writeClientAndServerModules( - serverShadowBlock = """ + serverShadowBlock = + """ minimize() - """.trimIndent(), + """ + .trimIndent() ) - path("server/src/main/java/server/Server.java").writeText( - """ + path("server/src/main/java/server/Server.java") + .writeText( + """ package server; import client.Client; public class Server { // This is to make sure that 'Client' is not removed. private final String client = Client.class.getName(); } - """.trimIndent(), - ) + """ + .trimIndent() + ) runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { - containsAtLeast( - "client/Client.class", - "server/Server.class", - ) - containsNone( - "junit/framework/Test.class", - ) + containsAtLeast("client/Client.class", "server/Server.class") + containsNone("junit/framework/Test.class") } } /** - * 'Client', 'Server' and 'junit' are independent. - * 'junit' is excluded from the minimize step. - * The minimize step shall remove 'Client' but not 'junit'. + * 'Client', 'Server' and 'junit' are independent. 'junit' is excluded from the minimize step. The + * minimize step shall remove 'Client' but not 'junit'. */ @Test fun excludeDependencyFromMinimize() { writeClientAndServerModules( - serverShadowBlock = """ + serverShadowBlock = + """ minimize { exclude(dependency('junit:junit:.*')) } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { - containsAtLeast( - "server/Server.class", - *junitEntries, - ) - containsNone( - "client/Client.class", - ) + containsAtLeast("server/Server.class", *junitEntries) + containsNone("client/Client.class") } } /** - * 'Client', 'Server' and 'junit' are independent. - * Unused classes of 'client' and theirs dependencies shouldn't be removed. + * 'Client', 'Server' and 'junit' are independent. Unused classes of 'client' and theirs + * dependencies shouldn't be removed. */ - @Issue( - "https://github.com/GradleUp/shadow/issues/744", - ) + @Issue("https://github.com/GradleUp/shadow/issues/744") @Test fun excludeProjectFromMinimize() { writeClientAndServerModules( - serverShadowBlock = """ + serverShadowBlock = + """ minimize { exclude(project(':client')) } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(serverShadowJarPath) @@ -171,58 +166,54 @@ class MinimizeTest : BasePluginTest() { } /** - * 'Client', 'Server' and 'junit' are independent. - * Unused classes of 'client' and theirs dependencies shouldn't be removed. + * 'Client', 'Server' and 'junit' are independent. Unused classes of 'client' and theirs + * dependencies shouldn't be removed. */ @Test fun excludeProjectFromMinimizeShallNotExcludeTransitiveDependenciesThatAreUsedInSubproject() { writeClientAndServerModules( - serverShadowBlock = """ + serverShadowBlock = + """ minimize { exclude(project(':client')) } - """.trimIndent(), + """ + .trimIndent() ) - path("client/src/main/java/client/Client.java").writeText( - """ + path("client/src/main/java/client/Client.java") + .writeText( + """ package client; import junit.framework.TestCase; public class Client extends TestCase { public static void main(String[] args) {} } - """.trimIndent(), - ) + """ + .trimIndent() + ) runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { - containsAtLeast( - "client/Client.class", - "server/Server.class", - *junitEntries, - ) + containsAtLeast("client/Client.class", "server/Server.class", *junitEntries) } - path("client/src/main/java/client/Client.java").writeText( - """ + path("client/src/main/java/client/Client.java") + .writeText( + """ package client; public class Client {} - """.trimIndent(), - ) + """ + .trimIndent() + ) runWithSuccess(serverShadowJarPath) assertThat(outputServerShadowedJar).useAll { - containsAtLeast( - "client/Client.class", - "server/Server.class", - *junitEntries, - ) + containsAtLeast("client/Client.class", "server/Server.class", *junitEntries) } } - @Issue( - "https://github.com/GradleUp/shadow/issues/1610", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1610") @Test fun excludeCircularDependencies() { val dependency = "'my:e:1.0'" @@ -236,17 +227,14 @@ class MinimizeTest : BasePluginTest() { exclude(dependency($dependency)) } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "e.properties", - "f.properties", - *manifestEntries, - ) + containsOnly("e.properties", "f.properties", *manifestEntries) } } @@ -263,13 +251,8 @@ class MinimizeTest : BasePluginTest() { assertThat(outputServerShadowedJar).useAll { if (enable) { - containsAtLeast( - "server/Server.class", - *manifestEntries, - ) - containsNone( - "client/Client.class", - ) + containsAtLeast("server/Server.class", *manifestEntries) + containsNone("client/Client.class") } else { containsOnly( "client/", @@ -283,19 +266,19 @@ class MinimizeTest : BasePluginTest() { } } - @Issue( - "https://github.com/GradleUp/shadow/issues/1636", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1636") @Test fun minimizeBomDependency() { writeApiLibAndImplModules() - path("impl/build.gradle").appendText( - """ + path("impl/build.gradle") + .appendText( + """ dependencies { api platform('my:bom:1.0') } - """.trimIndent(), - ) + """ + .trimIndent() + ) runWithSuccess(":impl:$SHADOW_JAR_TASK_NAME") @@ -316,46 +299,58 @@ class MinimizeTest : BasePluginTest() { private fun writeApiLibAndImplModules() { settingsScript.appendText( """ - include 'api', 'lib', 'impl' - """.trimIndent() + lineSeparator, + include 'api', 'lib', 'impl' + """ + .trimIndent() + lineSeparator ) projectScript.writeText("") - path("lib/src/main/java/lib/LibEntity.java").writeText( - """ + path("lib/src/main/java/lib/LibEntity.java") + .writeText( + """ package lib; public interface LibEntity {} - """.trimIndent(), - ) - path("lib/src/main/java/lib/UnusedLibEntity.java").writeText( - """ + """ + .trimIndent() + ) + path("lib/src/main/java/lib/UnusedLibEntity.java") + .writeText( + """ package lib; public class UnusedLibEntity implements LibEntity {} - """.trimIndent(), - ) - path("lib/build.gradle").writeText( - """ + """ + .trimIndent() + ) + path("lib/build.gradle") + .writeText( + """ plugins { id 'java' } - """.trimIndent() + lineSeparator, - ) + """ + .trimIndent() + lineSeparator + ) - path("api/src/main/java/api/Entity.java").writeText( - """ + path("api/src/main/java/api/Entity.java") + .writeText( + """ package api; public interface Entity {} - """.trimIndent(), - ) - path("api/src/main/java/api/UnusedEntity.java").writeText( - """ + """ + .trimIndent() + ) + path("api/src/main/java/api/UnusedEntity.java") + .writeText( + """ package api; import lib.LibEntity; public class UnusedEntity implements LibEntity {} - """.trimIndent(), - ) - path("api/build.gradle").writeText( - """ + """ + .trimIndent() + ) + path("api/build.gradle") + .writeText( + """ plugins { id 'java' } @@ -363,18 +358,22 @@ class MinimizeTest : BasePluginTest() { implementation 'junit:junit:3.8.2' implementation project(':lib') } - """.trimIndent() + lineSeparator, - ) + """ + .trimIndent() + lineSeparator + ) - path("impl/src/main/java/impl/SimpleEntity.java").writeText( - """ + path("impl/src/main/java/impl/SimpleEntity.java") + .writeText( + """ package impl; import api.Entity; public class SimpleEntity implements Entity {} - """.trimIndent(), - ) - path("impl/build.gradle").writeText( - """ + """ + .trimIndent() + ) + path("impl/build.gradle") + .writeText( + """ ${getDefaultProjectBuildScript("java-library")} dependencies { api project(':api') @@ -382,7 +381,8 @@ class MinimizeTest : BasePluginTest() { $shadowJarTask { minimize() } - """.trimIndent() + lineSeparator, - ) + """ + .trimIndent() + lineSeparator + ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt index 181bd3711..754ce1caf 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/PublishingTest.kt @@ -51,8 +51,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource class PublishingTest : BasePluginTest() { - @TempDir - lateinit var remoteRepoPath: Path + @TempDir lateinit var remoteRepoPath: Path @BeforeEach override fun beforeEach() { @@ -63,17 +62,20 @@ class PublishingTest : BasePluginTest() { @DisabledOnOs( OS.WINDOWS, architectures = ["aarch64"], - disabledReason = "Cannot use toolchain on Windows ARM64", // TODO: remove when min Gradle is bumped to 9.2+ + disabledReason = + "Cannot use toolchain on Windows ARM64", // TODO: remove when min Gradle is bumped to 9.2+ ) @Test fun publishShadowJarWithCorrectTargetJvm() { projectScript.appendText( publishConfiguration( - shadowBlock = """ + shadowBlock = + """ archiveClassifier = '' archiveBaseName = 'maven-all' - """.trimIndent(), - ) + lineSeparator, + """ + .trimIndent() + ) + lineSeparator ) val assertions = { variantAttrs: Array> -> @@ -85,88 +87,98 @@ class PublishingTest : BasePluginTest() { assertions(shadowVariantAttrs) - val attrsWithoutTargetJvm = shadowVariantAttrs.filterNot { (name, _) -> - name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name - }.toTypedArray() + val attrsWithoutTargetJvm = + shadowVariantAttrs + .filterNot { (name, _) -> name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name } + .toTypedArray() val targetJvmAttr17 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "17" val targetJvmAttr11 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "11" val targetJvmAttr8 = TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to "8" settingsScript.prependText( """ - plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' - } - """.trimIndent() + lineSeparator, + plugins { + id 'org.gradle.toolchains.foojay-resolver-convention' + } + """ + .trimIndent() + lineSeparator ) projectScript.appendText( """ - java { - toolchain.languageVersion = JavaLanguageVersion.of(17) - } - """.trimIndent() + lineSeparator, + java { + toolchain.languageVersion = JavaLanguageVersion.of(17) + } + """ + .trimIndent() + lineSeparator ) assertions(attrsWithoutTargetJvm + targetJvmAttr17) projectScript.appendText( """ - java { - targetCompatibility = JavaVersion.VERSION_11 - } - """.trimIndent() + lineSeparator, + java { + targetCompatibility = JavaVersion.VERSION_11 + } + """ + .trimIndent() + lineSeparator ) assertions(attrsWithoutTargetJvm + targetJvmAttr11) projectScript.appendText( """ - java { - sourceCompatibility = JavaVersion.VERSION_1_8 - } - """.trimIndent() + lineSeparator, + java { + sourceCompatibility = JavaVersion.VERSION_1_8 + } + """ + .trimIndent() + lineSeparator ) // sourceCompatibility doesn't affect the target JVM version. assertions(attrsWithoutTargetJvm + targetJvmAttr11) projectScript.appendText( """ - tasks.named('compileJava') { - options.release = 8 - } - """.trimIndent() + lineSeparator, + tasks.named('compileJava') { + options.release = 8 + } + """ + .trimIndent() + lineSeparator ) // options.release flag is honored. assertions(attrsWithoutTargetJvm + targetJvmAttr8) } - @Issue( - "https://github.com/GradleUp/shadow/issues/1665", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1665") @Test fun dontInjectTargetJvmVersionWhenAutoTargetJvmDisabled() { projectScript.appendText( publishConfiguration( - projectBlock = """ + projectBlock = + """ java { disableAutoTargetJvm() } - """.trimIndent(), - shadowBlock = """ + """ + .trimIndent(), + shadowBlock = + """ archiveClassifier = '' archiveBaseName = 'maven-all' - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) val result = publish(infoArgument) - assertThat(result.output).contains( - "Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases.", - ) + assertThat(result.output) + .contains( + "Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases." + ) assertShadowVariantCommon( gmm = gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")), - variantAttrs = shadowVariantAttrs.filterNot { (name, _) -> - name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name - }.toTypedArray(), + variantAttrs = + shadowVariantAttrs + .filterNot { (name, _) -> name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name } + .toTypedArray(), ) } @@ -174,28 +186,34 @@ class PublishingTest : BasePluginTest() { fun dontInjectTargetJvmVersionWhenOptingOut() { projectScript.appendText( publishConfiguration( - projectBlock = """ + projectBlock = + """ shadow { addTargetJvmVersionAttribute = false } - """.trimIndent(), - shadowBlock = """ + """ + .trimIndent(), + shadowBlock = + """ archiveClassifier = '' archiveBaseName = 'maven-all' - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) val result = publish(infoArgument) - assertThat(result.output).contains( - "Skipping setting org.gradle.jvm.version attribute for shadowRuntimeElements configuration.", - ) + assertThat(result.output) + .contains( + "Skipping setting org.gradle.jvm.version attribute for shadowRuntimeElements configuration." + ) assertShadowVariantCommon( gmm = gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")), - variantAttrs = shadowVariantAttrs.filterNot { (name, _) -> - name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name - }.toTypedArray(), + variantAttrs = + shadowVariantAttrs + .filterNot { (name, _) -> name == TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name } + .toTypedArray(), ) } @@ -203,26 +221,32 @@ class PublishingTest : BasePluginTest() { fun overrideBundlingAttrInGradleMetadata() { projectScript.appendText( publishConfiguration( - projectBlock = """ + projectBlock = + """ shadow { bundlingAttribute = Bundling.EMBEDDED } - """.trimIndent(), - shadowBlock = """ + """ + .trimIndent(), + shadowBlock = + """ archiveClassifier = '' archiveBaseName = 'maven-all' - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) publish() assertShadowVariantCommon( gmm = gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")), - variantAttrs = commonVariantAttrs + arrayOf( - Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EMBEDDED, - Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, - ), + variantAttrs = + commonVariantAttrs + + arrayOf( + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EMBEDDED, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + ), ) } @@ -230,37 +254,42 @@ class PublishingTest : BasePluginTest() { fun publishShadowJarInsteadOfJar() { projectScript.appendText( publishConfiguration( - shadowBlock = """ + shadowBlock = + """ archiveClassifier = '' - """.trimIndent(), - publicationsBlock = """ + """ + .trimIndent(), + publicationsBlock = + """ shadow(MavenPublication) { from components.shadow } - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) publish() val artifactRoot = "my/maven/1.0" - assertThat(repoPath(artifactRoot).entries).containsOnly( - "maven-1.0.jar", - "maven-1.0.module", - "maven-1.0.pom", - "maven-1.0.jar.md5", - "maven-1.0.module.md5", - "maven-1.0.pom.md5", - "maven-1.0.jar.sha1", - "maven-1.0.module.sha1", - "maven-1.0.pom.sha1", - "maven-1.0.jar.sha256", - "maven-1.0.module.sha256", - "maven-1.0.pom.sha256", - "maven-1.0.jar.sha512", - "maven-1.0.module.sha512", - "maven-1.0.pom.sha512", - ) + assertThat(repoPath(artifactRoot).entries) + .containsOnly( + "maven-1.0.jar", + "maven-1.0.module", + "maven-1.0.pom", + "maven-1.0.jar.md5", + "maven-1.0.module.md5", + "maven-1.0.pom.md5", + "maven-1.0.jar.sha1", + "maven-1.0.module.sha1", + "maven-1.0.pom.sha1", + "maven-1.0.jar.sha256", + "maven-1.0.module.sha256", + "maven-1.0.pom.sha256", + "maven-1.0.jar.sha512", + "maven-1.0.module.sha512", + "maven-1.0.pom.sha512", + ) assertShadowJarCommon(repoJarPath("$artifactRoot/maven-1.0.jar")) assertPomCommon(repoPath("$artifactRoot/maven-1.0.pom")) assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("$artifactRoot/maven-1.0.module"))) @@ -270,32 +299,35 @@ class PublishingTest : BasePluginTest() { fun publishCustomShadowJar() { projectScript.appendText( publishConfiguration( - projectBlock = """ + projectBlock = + """ def testShadowJar = tasks.register('testShadowJar', ${ShadowJar::class.java.name}) { description = 'Create a combined JAR of project and test dependencies' archiveClassifier = 'tests' from sourceSets.named('test').map { it.output } configurations = project.configurations.named('testRuntimeClasspath').map { [it] } } - """.trimIndent(), - dependenciesBlock = """ + """ + .trimIndent(), + dependenciesBlock = + """ testImplementation 'junit:junit:3.8.2' - """.trimIndent(), - publicationsBlock = """ + """ + .trimIndent(), + publicationsBlock = + """ shadow(MavenPublication) { artifact testShadowJar } - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) publish() assertThat(repoJarPath("my/maven/1.0/maven-1.0-tests.jar")).useAll { - containsOnly( - *junitEntries, - *manifestEntries, - ) + containsOnly(*junitEntries, *manifestEntries) } } @@ -304,34 +336,43 @@ class PublishingTest : BasePluginTest() { writeGradlePluginModule() projectScript.appendText( publishConfiguration( - projectBlock = """ + projectBlock = + """ apply plugin: 'com.gradle.plugin-publish' group = 'my.plugin' version = '1.0' - """.trimIndent(), - shadowBlock = """ + """ + .trimIndent(), + shadowBlock = + """ archiveClassifier = '' - """.trimIndent(), - publicationsBlock = """ + """ + .trimIndent(), + publicationsBlock = + """ pluginMaven(MavenPublication) { artifactId = 'my-gradle-plugin' } - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) publish() val artifactRoot = "my/plugin/my-gradle-plugin/1.0" - assertThat(repoPath(artifactRoot).entries.filter { it.endsWith(".jar") }).containsOnly( - "my-gradle-plugin-1.0.jar", - "my-gradle-plugin-1.0-javadoc.jar", - "my-gradle-plugin-1.0-sources.jar", - ) + assertThat(repoPath(artifactRoot).entries.filter { it.endsWith(".jar") }) + .containsOnly( + "my-gradle-plugin-1.0.jar", + "my-gradle-plugin-1.0-javadoc.jar", + "my-gradle-plugin-1.0-sources.jar", + ) assertShadowJarCommon(repoJarPath("$artifactRoot/my-gradle-plugin-1.0.jar")) assertPomCommon(repoPath("$artifactRoot/my-gradle-plugin-1.0.pom")) - assertShadowVariantCommon(gmmAdapter.fromJson(repoPath("$artifactRoot/my-gradle-plugin-1.0.module"))) + assertShadowVariantCommon( + gmmAdapter.fromJson(repoPath("$artifactRoot/my-gradle-plugin-1.0.module")) + ) } @Issue( @@ -343,44 +384,51 @@ class PublishingTest : BasePluginTest() { fun publishShadowJarWithCustomArtifactName() { projectScript.appendText( publishConfiguration( - projectBlock = """ + projectBlock = + """ group = 'my-group' version = '2.0' - """.trimIndent(), - shadowBlock = """ + """ + .trimIndent(), + shadowBlock = + """ archiveClassifier = 'my-classifier' archiveExtension = 'my-ext' archiveBaseName = 'maven-all' - """.trimIndent(), - publicationsBlock = """ - shadow(MavenPublication) { - from components.shadow - artifactId = 'my-artifact' - } - """.trimIndent(), - ), + """ + .trimIndent(), + publicationsBlock = + """ + shadow(MavenPublication) { + from components.shadow + artifactId = 'my-artifact' + } + """ + .trimIndent(), + ) ) publish() val artifactRoot = "my-group/my-artifact/2.0" - assertThat(repoPath(artifactRoot).entries).containsOnly( - "my-artifact-2.0-my-classifier.my-ext.sha512", - "my-artifact-2.0-my-classifier.my-ext", - "my-artifact-2.0.pom.sha256", - "my-artifact-2.0.module", - "my-artifact-2.0.pom", - "my-artifact-2.0.module.sha256", - "my-artifact-2.0.module.sha1", - "my-artifact-2.0.module.md5", - "my-artifact-2.0.pom.sha512", - "my-artifact-2.0-my-classifier.my-ext.sha256", - "my-artifact-2.0.module.sha512", - "my-artifact-2.0-my-classifier.my-ext.sha1", - "my-artifact-2.0-my-classifier.my-ext.md5", - "my-artifact-2.0.pom.md5", - "my-artifact-2.0.pom.sha1", - ) + assertThat(repoPath(artifactRoot).entries) + .containsOnly( + "my-artifact-2.0-my-classifier.my-ext.sha512", + "my-artifact-2.0-my-classifier.my-ext", + "my-artifact-2.0.pom.sha256", + "my-artifact-2.0.module", + "my-artifact-2.0.pom", + "my-artifact-2.0.module.sha256", + "my-artifact-2.0.module.sha1", + "my-artifact-2.0.module.md5", + "my-artifact-2.0.pom.sha512", + "my-artifact-2.0-my-classifier.my-ext.sha256", + "my-artifact-2.0.module.sha512", + "my-artifact-2.0-my-classifier.my-ext.sha1", + "my-artifact-2.0-my-classifier.my-ext.md5", + "my-artifact-2.0.pom.md5", + "my-artifact-2.0.pom.sha1", + ) assertShadowJarCommon(repoJarPath("$artifactRoot/my-artifact-2.0-my-classifier.my-ext")) assertPomCommon(repoPath("$artifactRoot/my-artifact-2.0.pom")) @@ -391,12 +439,15 @@ class PublishingTest : BasePluginTest() { fun publishJarAndShadowJarWithGradleMetadata() { projectScript.appendText( publishConfiguration( - dependenciesBlock = """ + dependenciesBlock = + """ implementation 'my:a:1.0' implementation 'my:b:1.0' shadow 'my:b:1.0' - """.trimIndent(), - publicationsBlock = """ + """ + .trimIndent(), + publicationsBlock = + """ java(MavenPublication) { from components.java } @@ -404,112 +455,107 @@ class PublishingTest : BasePluginTest() { from components.shadow artifactId = "maven-all" } - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) publish() - assertThat(repoPath("my/maven/1.0").entries).containsOnly( - // Entries of maven-1.0.jar - "maven-1.0.jar", - "maven-1.0.module", - "maven-1.0.pom", - "maven-1.0.jar.md5", - "maven-1.0.module.md5", - "maven-1.0.pom.md5", - "maven-1.0.jar.sha1", - "maven-1.0.module.sha1", - "maven-1.0.pom.sha1", - "maven-1.0.jar.sha256", - "maven-1.0.module.sha256", - "maven-1.0.pom.sha256", - "maven-1.0.jar.sha512", - "maven-1.0.module.sha512", - "maven-1.0.pom.sha512", - // Entries of maven-1.0-all.jar - "maven-1.0-all.jar", - "maven-1.0-all.jar.md5", - "maven-1.0-all.jar.sha1", - "maven-1.0-all.jar.sha256", - "maven-1.0-all.jar.sha512", - ) - assertThat(repoPath("my/maven-all/1.0").entries).containsOnly( - "maven-all-1.0-all.jar", - "maven-all-1.0.module", - "maven-all-1.0.pom", - "maven-all-1.0-all.jar.md5", - "maven-all-1.0.module.md5", - "maven-all-1.0.pom.md5", - "maven-all-1.0-all.jar.sha1", - "maven-all-1.0.module.sha1", - "maven-all-1.0.pom.sha1", - "maven-all-1.0-all.jar.sha256", - "maven-all-1.0.module.sha256", - "maven-all-1.0.pom.sha256", - "maven-all-1.0-all.jar.sha512", - "maven-all-1.0.module.sha512", - "maven-all-1.0.pom.sha512", - ) + assertThat(repoPath("my/maven/1.0").entries) + .containsOnly( + // Entries of maven-1.0.jar + "maven-1.0.jar", + "maven-1.0.module", + "maven-1.0.pom", + "maven-1.0.jar.md5", + "maven-1.0.module.md5", + "maven-1.0.pom.md5", + "maven-1.0.jar.sha1", + "maven-1.0.module.sha1", + "maven-1.0.pom.sha1", + "maven-1.0.jar.sha256", + "maven-1.0.module.sha256", + "maven-1.0.pom.sha256", + "maven-1.0.jar.sha512", + "maven-1.0.module.sha512", + "maven-1.0.pom.sha512", + // Entries of maven-1.0-all.jar + "maven-1.0-all.jar", + "maven-1.0-all.jar.md5", + "maven-1.0-all.jar.sha1", + "maven-1.0-all.jar.sha256", + "maven-1.0-all.jar.sha512", + ) + assertThat(repoPath("my/maven-all/1.0").entries) + .containsOnly( + "maven-all-1.0-all.jar", + "maven-all-1.0.module", + "maven-all-1.0.pom", + "maven-all-1.0-all.jar.md5", + "maven-all-1.0.module.md5", + "maven-all-1.0.pom.md5", + "maven-all-1.0-all.jar.sha1", + "maven-all-1.0.module.sha1", + "maven-all-1.0.pom.sha1", + "maven-all-1.0-all.jar.sha256", + "maven-all-1.0.module.sha256", + "maven-all-1.0.pom.sha256", + "maven-all-1.0-all.jar.sha512", + "maven-all-1.0.module.sha512", + "maven-all-1.0.pom.sha512", + ) - assertThat(repoJarPath("my/maven/1.0/maven-1.0.jar")).useAll { - containsNone(*entriesInAB) - } + assertThat(repoJarPath("my/maven/1.0/maven-1.0.jar")).useAll { containsNone(*entriesInAB) } assertThat(repoJarPath("my/maven/1.0/maven-1.0-all.jar")).useAll { - containsOnly( - *entriesInAB, - *manifestEntries, - ) + containsOnly(*entriesInAB, *manifestEntries) } assertPomCommon(repoPath("my/maven/1.0/maven-1.0.pom"), arrayOf("my:a:1.0", "my:b:1.0")) gmmAdapter.fromJson(repoPath("my/maven/1.0/maven-1.0.module")).let { gmm -> // apiElements, runtimeElements, shadowRuntimeElements - assertThat(gmm.variantNames).containsOnly( - API_ELEMENTS_CONFIGURATION_NAME, - RUNTIME_ELEMENTS_CONFIGURATION_NAME, - SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, - ) - assertThat(gmm.apiElementsVariant).all { - transform { it.attributes }.containsOnly( - *commonVariantAttrs, - Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, - Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_API, + assertThat(gmm.variantNames) + .containsOnly( + API_ELEMENTS_CONFIGURATION_NAME, + RUNTIME_ELEMENTS_CONFIGURATION_NAME, + SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, ) + assertThat(gmm.apiElementsVariant).all { + transform { it.attributes } + .containsOnly( + *commonVariantAttrs, + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_API, + ) transform { it.coordinates }.isEmpty() } assertThat(gmm.runtimeElementsVariant).all { - transform { it.attributes }.containsOnly( - *commonVariantAttrs, - Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, - Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, - ) - transform { it.coordinates }.containsOnly( - "my:a:1.0", - "my:b:1.0", - ) + transform { it.attributes } + .containsOnly( + *commonVariantAttrs, + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + ) + transform { it.coordinates }.containsOnly("my:a:1.0", "my:b:1.0") } assertShadowVariantCommon(gmm) } assertPomCommon(repoPath("my/maven-all/1.0/maven-all-1.0.pom")) gmmAdapter.fromJson(repoPath("my/maven-all/1.0/maven-all-1.0.module")).let { gmm -> - assertThat(gmm.variantNames).containsOnly( - SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, - ) + assertThat(gmm.variantNames).containsOnly(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) assertShadowVariantCommon(gmm) } } - @Issue( - "https://github.com/GradleUp/shadow/issues/651", - ) + @Issue("https://github.com/GradleUp/shadow/issues/651") @ParameterizedTest @ValueSource(booleans = [false, true]) fun publishShadowVariantJar(addShadowVariant: Boolean) { projectScript.appendText( publishingBlock( - projectBlock = """ + projectBlock = + """ dependencies { implementation 'my:a:1.0' shadow 'my:b:1.0' @@ -517,103 +563,103 @@ class PublishingTest : BasePluginTest() { shadow { addShadowVariantIntoJavaComponent = $addShadowVariant } - """.trimIndent(), - publicationsBlock = """ + """ + .trimIndent(), + publicationsBlock = + """ shadow(MavenPublication) { from components.java } - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) val result = publish(infoArgument) - assertThat(result.output).contains( - if (addShadowVariant) { - "Adding shadowRuntimeElements variant to Java component." - } else { - "Skipping adding shadowRuntimeElements variant to Java component." - }, - ) + assertThat(result.output) + .contains( + if (addShadowVariant) { + "Adding shadowRuntimeElements variant to Java component." + } else { + "Skipping adding shadowRuntimeElements variant to Java component." + } + ) val assertVariantsCommon = { gmm: GradleModuleMetadata -> assertThat(gmm.apiElementsVariant).all { - transform { it.attributes }.containsOnly( - *commonVariantAttrs, - Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, - Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_API, - ) + transform { it.attributes } + .containsOnly( + *commonVariantAttrs, + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_API, + ) transform { it.coordinates }.isEmpty() } assertThat(gmm.runtimeElementsVariant).all { - transform { it.attributes }.containsOnly( - *commonVariantAttrs, - Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, - Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, - ) - transform { it.coordinates }.containsOnly( - "my:a:1.0", - ) + transform { it.attributes } + .containsOnly( + *commonVariantAttrs, + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.EXTERNAL, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + ) + transform { it.coordinates }.containsOnly("my:a:1.0") } } - val entriesCommon = arrayOf( - "maven-1.0.jar", - "maven-1.0.jar.md5", - "maven-1.0.jar.sha1", - "maven-1.0.jar.sha256", - "maven-1.0.jar.sha512", - "maven-1.0.module", - "maven-1.0.module.md5", - "maven-1.0.module.sha1", - "maven-1.0.module.sha256", - "maven-1.0.module.sha512", - "maven-1.0.pom", - "maven-1.0.pom.md5", - "maven-1.0.pom.sha1", - "maven-1.0.pom.sha256", - "maven-1.0.pom.sha512", - ) + val entriesCommon = + arrayOf( + "maven-1.0.jar", + "maven-1.0.jar.md5", + "maven-1.0.jar.sha1", + "maven-1.0.jar.sha256", + "maven-1.0.jar.sha512", + "maven-1.0.module", + "maven-1.0.module.md5", + "maven-1.0.module.sha1", + "maven-1.0.module.sha256", + "maven-1.0.module.sha512", + "maven-1.0.pom", + "maven-1.0.pom.md5", + "maven-1.0.pom.sha1", + "maven-1.0.pom.sha256", + "maven-1.0.pom.sha512", + ) val artifactEntries = repoPath("my/maven/1.0/").entries val gmm = gmmAdapter.fromJson(repoPath("my/maven/1.0/maven-1.0.module")) - val pomDependencies = pomReader.read(repoPath("my/maven/1.0/maven-1.0.pom")) - .dependencies.map { it.coordinate to it.scope } + val pomDependencies = + pomReader.read(repoPath("my/maven/1.0/maven-1.0.pom")).dependencies.map { + it.coordinate to it.scope + } if (addShadowVariant) { - assertThat(artifactEntries).containsOnly( - "maven-1.0-all.jar", - "maven-1.0-all.jar.md5", - "maven-1.0-all.jar.sha1", - "maven-1.0-all.jar.sha256", - "maven-1.0-all.jar.sha512", - *entriesCommon, - ) - assertThat(gmm.variantNames).containsOnly( - API_ELEMENTS_CONFIGURATION_NAME, - RUNTIME_ELEMENTS_CONFIGURATION_NAME, - SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, - ) + assertThat(artifactEntries) + .containsOnly( + "maven-1.0-all.jar", + "maven-1.0-all.jar.md5", + "maven-1.0-all.jar.sha1", + "maven-1.0-all.jar.sha256", + "maven-1.0-all.jar.sha512", + *entriesCommon, + ) + assertThat(gmm.variantNames) + .containsOnly( + API_ELEMENTS_CONFIGURATION_NAME, + RUNTIME_ELEMENTS_CONFIGURATION_NAME, + SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME, + ) assertVariantsCommon(gmm) assertShadowVariantCommon(gmm) - assertThat(pomDependencies).containsOnly( - "my:a:1.0" to "runtime", - "my:b:1.0" to "compile", - ) + assertThat(pomDependencies).containsOnly("my:a:1.0" to "runtime", "my:b:1.0" to "compile") } else { assertThat(artifactEntries).containsOnly(*entriesCommon) - assertThat(gmm.variantNames).containsOnly( - API_ELEMENTS_CONFIGURATION_NAME, - RUNTIME_ELEMENTS_CONFIGURATION_NAME, - ) + assertThat(gmm.variantNames) + .containsOnly(API_ELEMENTS_CONFIGURATION_NAME, RUNTIME_ELEMENTS_CONFIGURATION_NAME) assertVariantsCommon(gmm) - assertThat(pomDependencies).containsOnly( - "my:a:1.0" to "runtime", - ) + assertThat(pomDependencies).containsOnly("my:a:1.0" to "runtime") } } private fun repoPath(relative: String): Path { - return remoteRepoPath.resolve(relative).also { - check(it.exists()) { "Path not found: $it" } - } + return remoteRepoPath.resolve(relative).also { check(it.exists()) { "Path not found: $it" } } } private fun repoJarPath(relative: String): JarPath { @@ -624,17 +670,21 @@ class PublishingTest : BasePluginTest() { private fun publishConfiguration( projectBlock: String = "", - dependenciesBlock: String = """ + dependenciesBlock: String = + """ implementation 'my:a:1.0' shadow 'my:b:1.0' - """.trimIndent(), + """ + .trimIndent(), shadowBlock: String = "", - publicationsBlock: String = """ + publicationsBlock: String = + """ shadow(MavenPublication) { from components.shadow artifactId = 'maven-all' } - """.trimIndent(), + """ + .trimIndent(), ): String { return """ dependencies { @@ -644,13 +694,11 @@ class PublishingTest : BasePluginTest() { $shadowBlock } ${publishingBlock(projectBlock = projectBlock, publicationsBlock = publicationsBlock)} - """.trimIndent() + """ + .trimIndent() } - private fun publishingBlock( - projectBlock: String, - publicationsBlock: String, - ): String { + private fun publishingBlock(projectBlock: String, publicationsBlock: String): String { return """ apply plugin: 'maven-publish' $projectBlock @@ -662,17 +710,17 @@ class PublishingTest : BasePluginTest() { maven { url = '${remoteRepoPath.toUri()}' } } } - """.trimIndent() + """ + .trimIndent() } - private fun assertPomCommon( - pomPath: Path, - coordinates: Array = arrayOf("my:b:1.0"), - ) { + private fun assertPomCommon(pomPath: Path, coordinates: Array = arrayOf("my:b:1.0")) { assertThat(pomReader.read(pomPath)).all { transform { it.dependencies.map(Dependency::coordinate) }.containsOnly(*coordinates) // All scopes should be runtime. - transform { it.dependencies.map(Dependency::getScope).distinct() }.single().isEqualTo("runtime") + transform { it.dependencies.map(Dependency::getScope).distinct() } + .single() + .isEqualTo("runtime") } } @@ -698,25 +746,32 @@ class PublishingTest : BasePluginTest() { } private companion object { - val gmmAdapter: JsonAdapter = Moshi.Builder().add(KotlinJsonAdapterFactory()).build() - .adapter(GradleModuleMetadata::class.java) + val gmmAdapter: JsonAdapter = + Moshi.Builder() + .add(KotlinJsonAdapterFactory()) + .build() + .adapter(GradleModuleMetadata::class.java) val pomReader = MavenXpp3Reader() - val commonVariantAttrs = arrayOf( - Category.CATEGORY_ATTRIBUTE.name to Category.LIBRARY, - LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name to LibraryElements.JAR, - TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to JavaVersion.current().majorVersion, - ) + val commonVariantAttrs = + arrayOf( + Category.CATEGORY_ATTRIBUTE.name to Category.LIBRARY, + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE.name to LibraryElements.JAR, + TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name to JavaVersion.current().majorVersion, + ) - val shadowVariantAttrs = commonVariantAttrs + arrayOf( - Bundling.BUNDLING_ATTRIBUTE.name to Bundling.SHADOWED, - Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, - ) + val shadowVariantAttrs = + commonVariantAttrs + + arrayOf( + Bundling.BUNDLING_ATTRIBUTE.name to Bundling.SHADOWED, + Usage.USAGE_ATTRIBUTE.name to Usage.JAVA_RUNTIME, + ) fun MavenXpp3Reader.read(path: Path): Model = path.inputStream().use { read(it) } fun JsonAdapter.fromJson(path: Path): T = checkNotNull(fromJson(path.readText())) - val Path.entries: List get() = listDirectoryEntries().map { it.name } + val Path.entries: List + get() = listDirectoryEntries().map { it.name } } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt index 9a1244388..852ebcc79 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/RelocationTest.kt @@ -45,35 +45,32 @@ class RelocationTest : BasePluginTest() { enableAutoRelocation = true relocationPrefix = '$relocationPrefix' } - """.trimIndent(), + """ + .trimIndent() ) val entryPrefix = relocationPrefix.replace('.', '/') - val relocatedEntries = buildSet { - addAll( - junitEntries.map { "$entryPrefix/$it" } - .filterNot { it.startsWith("$entryPrefix/META-INF/") }, - ) - var parent = entryPrefix - while (parent.isNotEmpty()) { - add("$parent/") - parent = parent.substringBeforeLast('/', "") - } - }.toTypedArray() + val relocatedEntries = + buildSet { + addAll( + junitEntries + .map { "$entryPrefix/$it" } + .filterNot { it.startsWith("$entryPrefix/META-INF/") } + ) + var parent = entryPrefix + while (parent.isNotEmpty()) { + add("$parent/") + parent = parent.substringBeforeLast('/', "") + } + } + .toTypedArray() val result = runWithSuccess(shadowJarPath, infoArgument) assertThat(outputShadowedJar).useAll { - containsOnly( - "my/", - mainClassEntry, - *relocatedEntries, - *manifestEntries, - ) + containsOnly("my/", mainClassEntry, *relocatedEntries, *manifestEntries) } // Make sure the relocator count is aligned with the number of unique packages in junit jar. - assertThat(result.output).contains( - "Relocator count: 6.", - ) + assertThat(result.output).contains("Relocator count: 6.") } @ParameterizedTest @@ -82,45 +79,43 @@ class RelocationTest : BasePluginTest() { val mainClassEntry = writeClass() projectScript.appendText( """ - dependencies { - implementation 'junit:junit:3.8.2' - } - """.trimIndent(), + dependencies { + implementation 'junit:junit:3.8.2' + } + """ + .trimIndent() ) - val relocatedEntries = junitEntries.map { "$relocationPrefix/$it" } - .filterNot { it.startsWith("$relocationPrefix/META-INF/") } - .toTypedArray() + val relocatedEntries = + junitEntries + .map { "$relocationPrefix/$it" } + .filterNot { it.startsWith("$relocationPrefix/META-INF/") } + .toTypedArray() if (enable) { - runWithSuccess(shadowJarPath, "--enable-auto-relocation", "--relocation-prefix=$relocationPrefix") + runWithSuccess( + shadowJarPath, + "--enable-auto-relocation", + "--relocation-prefix=$relocationPrefix", + ) } else { - runWithSuccess(shadowJarPath, "--no-enable-auto-relocation", "--relocation-prefix=$relocationPrefix") + runWithSuccess( + shadowJarPath, + "--no-enable-auto-relocation", + "--relocation-prefix=$relocationPrefix", + ) } - val commonEntries = arrayOf( - "my/", - mainClassEntry, - *manifestEntries, - ) + val commonEntries = arrayOf("my/", mainClassEntry, *manifestEntries) assertThat(outputShadowedJar).useAll { if (enable) { - containsOnly( - "$relocationPrefix/", - *relocatedEntries, - *commonEntries, - ) + containsOnly("$relocationPrefix/", *relocatedEntries, *commonEntries) } else { - containsOnly( - *junitEntries, - *commonEntries, - ) + containsOnly(*junitEntries, *commonEntries) } } } - @Issue( - "https://github.com/GradleUp/shadow/issues/58", - ) + @Issue("https://github.com/GradleUp/shadow/issues/58") @Test fun relocateDependencyFiles() { val mainClassEntry = writeClass() @@ -133,17 +128,20 @@ class RelocationTest : BasePluginTest() { relocate 'junit.runner', 'a' relocate 'junit.framework', 'b' } - """.trimIndent(), + """ + .trimIndent() ) val runnerFilter = { it: String -> it.startsWith("junit/runner/") } val frameworkFilter = { it: String -> it.startsWith("junit/framework/") } - val runnerEntries = junitEntries - .filter(runnerFilter) - .map { it.replace("junit/runner/", "a/") }.toTypedArray() - val frameworkEntries = junitEntries - .filter(frameworkFilter) - .map { it.replace("junit/framework/", "b/") }.toTypedArray() - val otherJunitEntries = junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() + val runnerEntries = + junitEntries.filter(runnerFilter).map { it.replace("junit/runner/", "a/") }.toTypedArray() + val frameworkEntries = + junitEntries + .filter(frameworkFilter) + .map { it.replace("junit/framework/", "b/") } + .toTypedArray() + val otherJunitEntries = + junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() runWithSuccess(shadowJarPath) @@ -175,17 +173,22 @@ class RelocationTest : BasePluginTest() { include 'junit.framework.Test*' } } - """.trimIndent(), + """ + .trimIndent() ) - val runnerFilter = { it: String -> it.startsWith("junit/runner/") && it != "junit/runner/BaseTestRunner.class" } + val runnerFilter = { it: String -> + it.startsWith("junit/runner/") && it != "junit/runner/BaseTestRunner.class" + } val frameworkFilter = { it: String -> it.startsWith("junit/framework/Test") } - val runnerEntries = junitEntries - .filter(runnerFilter) - .map { it.replace("junit/runner/", "a/") }.toTypedArray() - val frameworkEntries = junitEntries - .filter(frameworkFilter) - .map { it.replace("junit/framework/", "b/") }.toTypedArray() - val otherJunitEntries = junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() + val runnerEntries = + junitEntries.filter(runnerFilter).map { it.replace("junit/runner/", "a/") }.toTypedArray() + val frameworkEntries = + junitEntries + .filter(frameworkFilter) + .map { it.replace("junit/framework/", "b/") } + .toTypedArray() + val otherJunitEntries = + junitEntries.filterNot { runnerFilter(it) || frameworkFilter(it) }.toTypedArray() runWithSuccess(shadowJarPath) @@ -217,13 +220,15 @@ class RelocationTest : BasePluginTest() { $shadowJarTask { relocate 'junit.framework', 'shadow.junit' } - """.trimIndent(), + """ + .trimIndent() ) - val relocatedEntries = junitEntries - .map { it.replace("junit/framework/", "shadow/junit/") }.toTypedArray() + val relocatedEntries = + junitEntries.map { it.replace("junit/framework/", "shadow/junit/") }.toTypedArray() - path("src/main/java/my/MyTest.java").writeText( - """ + path("src/main/java/my/MyTest.java") + .writeText( + """ package my; import junit.framework.Test; import junit.framework.TestResult; @@ -231,29 +236,27 @@ class RelocationTest : BasePluginTest() { public int countTestCases() { return 0; } public void run(TestResult result) { } } - """.trimIndent(), - ) + """ + .trimIndent() + ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "my/", - "shadow/", - "my/MyTest.class", - *relocatedEntries, - *manifestEntries, - ) + containsOnly("my/", "shadow/", "my/MyTest.class", *relocatedEntries, *manifestEntries) } val url = outputShadowedJar.use { it.toUri().toURL() } URLClassLoader(arrayOf(url), ClassLoader.getSystemClassLoader().parent).use { classLoader -> assertFailure { - // check that the class can be loaded. If the file was not relocated properly, we should get a NoDefClassFound - // Isolated class loader with only the JVM system jars and the output jar from the test project - classLoader.loadClass("my.MyTest") - fail("Should not reach here.") - }.isInstanceOf(AssertionFailedError::class) + // check that the class can be loaded. If the file was not relocated properly, we should + // get a NoDefClassFound + // Isolated class loader with only the JVM system jars and the output jar from the test + // project + classLoader.loadClass("my.MyTest") + fail("Should not reach here.") + } + .isInstanceOf(AssertionFailedError::class) } } @@ -263,9 +266,7 @@ class RelocationTest : BasePluginTest() { ) @Test fun relocateResourceFiles() { - val depJar = buildJar("foo.jar") { - insert("foo/dep.properties", "c") - } + val depJar = buildJar("foo.jar") { insert("foo/dep.properties", "c") } writeClass(packageName = "foo", className = "Foo") path("src/main/resources/foo/foo.properties").writeText("name=foo") @@ -277,7 +278,8 @@ class RelocationTest : BasePluginTest() { $shadowJarTask { relocate 'foo', 'bar' } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -295,7 +297,10 @@ class RelocationTest : BasePluginTest() { @ParameterizedTest @MethodSource("preserveLastModifiedProvider") - fun preserveLastModifiedCorrectly(enableAutoRelocation: Boolean, preserveFileTimestamps: Boolean) { + fun preserveLastModifiedCorrectly( + enableAutoRelocation: Boolean, + preserveFileTimestamps: Boolean, + ) { // Minus 3 sec to avoid the time difference between the file system and the JVM. val currentTimeMillis = System.currentTimeMillis() - 3.seconds.inWholeMilliseconds val junitEntryTimeRange = junitRawEntries.map { it.time }.let { it.min()..it.max() } @@ -309,15 +314,17 @@ class RelocationTest : BasePluginTest() { enableAutoRelocation = $enableAutoRelocation preserveFileTimestamps = $preserveFileTimestamps } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) if (enableAutoRelocation) { - val (relocatedEntries, otherEntries) = outputShadowedJar.use { - it.entries().toList().partition { entry -> entry.name.startsWith("shadow/") } - } + val (relocatedEntries, otherEntries) = + outputShadowedJar.use { + it.entries().toList().partition { entry -> entry.name.startsWith("shadow/") } + } assertThat(relocatedEntries).isNotEmpty() assertThat(otherEntries).isNotEmpty() val (relocatedDirs, relocatedClasses) = relocatedEntries.partition { it.isDirectory } @@ -332,9 +339,12 @@ class RelocationTest : BasePluginTest() { } } (relocatedDirs + otherEntries).forEach { entry -> - // Relocated directories and other entries are newly created, so they should be in now time. + // Relocated directories and other entries are newly created, so they should be in now + // time. if (entry.time < currentTimeMillis) { - fail("Relocated directory ${entry.name} has an invalid last modified time: ${entry.time}") + fail( + "Relocated directory ${entry.name} has an invalid last modified time: ${entry.time}" + ) } } } else { @@ -346,9 +356,10 @@ class RelocationTest : BasePluginTest() { } } } else { - val (shadowedEntries, otherEntries) = outputShadowedJar.use { - it.entries().toList().partition { entry -> entry.name.startsWith("junit/") } - } + val (shadowedEntries, otherEntries) = + outputShadowedJar.use { + it.entries().toList().partition { entry -> entry.name.startsWith("junit/") } + } assertThat(shadowedEntries).isNotEmpty() assertThat(otherEntries).isNotEmpty() @@ -383,9 +394,10 @@ class RelocationTest : BasePluginTest() { ) @Test fun excludeKotlinBuiltinsFromRelocation() { - val kotlinJar = buildJar("kotlin.jar") { - insert("kotlin/kotlin.kotlin_builtins", "This is a Kotlin builtins file.") - } + val kotlinJar = + buildJar("kotlin.jar") { + insert("kotlin/kotlin.kotlin_builtins", "This is a Kotlin builtins file.") + } projectScript.appendText( """ dependencies { @@ -396,31 +408,30 @@ class RelocationTest : BasePluginTest() { exclude('kotlin/kotlin.kotlin_builtins') } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "kotlin/", - "kotlin/kotlin.kotlin_builtins", - *manifestEntries, - ) + containsOnly("kotlin/", "kotlin/kotlin.kotlin_builtins", *manifestEntries) } } @ParameterizedTest @ValueSource(booleans = [false, true]) fun relocateAllPackagesButCertainOne(exclude: Boolean) { - val relocateConfig = if (exclude) { - """ + val relocateConfig = + if (exclude) { + """ exclude 'junit/**' exclude 'META-INF/**' - """.trimIndent() - } else { - "" - } + """ + .trimIndent() + } else { + "" + } projectScript.appendText( """ dependencies { @@ -431,23 +442,17 @@ class RelocationTest : BasePluginTest() { $relocateConfig } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { if (exclude) { - containsOnly( - *junitEntries, - *manifestEntries, - ) + containsOnly(*junitEntries, *manifestEntries) } else { - containsOnly( - "foo/", - "foo/$manifestEntry", - *junitEntries.map { "foo/$it" }.toTypedArray(), - ) + containsOnly("foo/", "foo/$manifestEntry", *junitEntries.map { "foo/$it" }.toTypedArray()) } } } @@ -464,19 +469,14 @@ class RelocationTest : BasePluginTest() { configurations = [] relocate('', 'foo/') } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "foo/", - "foo/my/", - "foo/META-INF/", - "foo/$mainClassEntry", - "foo/$manifestEntry", - ) + containsOnly("foo/", "foo/my/", "foo/META-INF/", "foo/$mainClassEntry", "foo/$manifestEntry") } } @@ -491,16 +491,14 @@ class RelocationTest : BasePluginTest() { } relocate('foo', 'shadow.foo') } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) - assertThat(result).contains( - "shadow.foo.Foo", - "shadow.foo.Bar", - ) + assertThat(result).contains("shadow.foo.Foo", "shadow.foo.Bar") } @Issue( @@ -521,41 +519,35 @@ class RelocationTest : BasePluginTest() { skipStringConstants = $skipStringConstants } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) if (skipStringConstants) { - assertThat(result).contains( - "foo.Foo", - "foo.Bar", - ) + assertThat(result).contains("foo.Foo", "foo.Bar") } else { - assertThat(result).contains( - "shadow.foo.Foo", - "shadow.foo.Bar", - ) + assertThat(result).contains("shadow.foo.Foo", "shadow.foo.Bar") } } - @Issue( - "https://github.com/GradleUp/shadow/issues/1403", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1403") @Test fun relocateMultiClassSignatureStringConstants() { writeClass { """ - package my; - public class Main { - public static void main(String[] args) { - System.out.println("Lorg/package/ClassA;Lorg/package/ClassB;"); - System.out.println("(Lorg/package/ClassC;Lorg/package/ClassD;)"); - System.out.println("()Lorg/package/ClassE;Lorg/package/ClassF;"); - } + package my; + public class Main { + public static void main(String[] args) { + System.out.println("Lorg/package/ClassA;Lorg/package/ClassB;"); + System.out.println("(Lorg/package/ClassC;Lorg/package/ClassD;)"); + System.out.println("()Lorg/package/ClassE;Lorg/package/ClassF;"); } - """.trimIndent() + } + """ + .trimIndent() } projectScript.appendText( """ @@ -565,18 +557,20 @@ class RelocationTest : BasePluginTest() { } relocate('org.package', 'shadow.org.package') } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) val result = runProcess("java", "-jar", outputShadowedJar.use { it.toString() }) // Just check that the jar can be executed without NoClassDefFoundError. - assertThat(result).contains( - "Lshadow/org/package/ClassA;Lshadow/org/package/ClassB", - "Lshadow/org/package/ClassC;Lshadow/org/package/ClassD", - "Lshadow/org/package/ClassE;Lshadow/org/package/ClassF", - ) + assertThat(result) + .contains( + "Lshadow/org/package/ClassA;Lshadow/org/package/ClassB", + "Lshadow/org/package/ClassC;Lshadow/org/package/ClassD", + "Lshadow/org/package/ClassE;Lshadow/org/package/ClassF", + ) } @Test @@ -590,7 +584,8 @@ class RelocationTest : BasePluginTest() { $shadowJarTask { enableAutoRelocation = true } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(":jar", shadowJarPath) @@ -600,18 +595,15 @@ class RelocationTest : BasePluginTest() { assertThat(relocatedBytes).isEqualTo(originalBytes) } - @Issue( - "https://github.com/GradleUp/shadow/issues/843", - ) + @Issue("https://github.com/GradleUp/shadow/issues/843") @OptIn(UnstableMetadataApi::class) @ParameterizedTest @ValueSource(booleans = [false, true]) fun relocateKotlinModuleFiles(enableKotlinModuleRemapping: Boolean) { val originalModuleFilePath = "META-INF/kotlin-stdlib.kotlin_module" val originalModuleFileBytes = requireResourceAsPath(originalModuleFilePath).readBytes() - val stdlibJar = buildJar("stdlib.jar") { - insert(originalModuleFilePath, originalModuleFileBytes) - } + val stdlibJar = + buildJar("stdlib.jar") { insert(originalModuleFilePath, originalModuleFileBytes) } projectScript.appendText( """ dependencies { @@ -621,7 +613,8 @@ class RelocationTest : BasePluginTest() { relocate('kotlin', 'my.kotlin') enableKotlinModuleRemapping = $enableKotlinModuleRemapping } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -630,26 +623,21 @@ class RelocationTest : BasePluginTest() { if (enableKotlinModuleRemapping) { assertThat(outputShadowedJar).useAll { - containsOnly( - relocatedModuleFilePath, - *manifestEntries, - ) + containsOnly(relocatedModuleFilePath, *manifestEntries) } } else { assertThat(outputShadowedJar).useAll { - containsOnly( - originalModuleFilePath, - *manifestEntries, - ) + containsOnly(originalModuleFilePath, *manifestEntries) } - assertThat(outputShadowedJar.use { it.getBytes(originalModuleFilePath) }).isEqualTo(originalModuleFileBytes) + assertThat(outputShadowedJar.use { it.getBytes(originalModuleFilePath) }) + .isEqualTo(originalModuleFileBytes) return } - val originalModule = KotlinModuleMetadata.read(requireResourceAsStream(originalModuleFilePath).readBytes()) - val relocatedModule = outputShadowedJar.use { - KotlinModuleMetadata.read(it.getBytes(relocatedModuleFilePath)) - } + val originalModule = + KotlinModuleMetadata.read(requireResourceAsStream(originalModuleFilePath).readBytes()) + val relocatedModule = + outputShadowedJar.use { KotlinModuleMetadata.read(it.getBytes(relocatedModuleFilePath)) } assertThat(relocatedModule.version.toString()).isEqualTo("2.2.0") assertThat(originalModule.version.toString()).isEqualTo("2.2.0") @@ -674,22 +662,23 @@ class RelocationTest : BasePluginTest() { } else { assertThat(relocatedParts.fileFacades).isNotEmpty() assertThat(relocatedParts.fileFacades).isNotEqualTo(originalParts.fileFacades) - assertThat(relocatedParts.fileFacades).isEqualTo( - originalParts.fileFacades.map { it.replace("kotlin/", "my/kotlin/") }, - ) + assertThat(relocatedParts.fileFacades) + .isEqualTo(originalParts.fileFacades.map { it.replace("kotlin/", "my/kotlin/") }) } if (originalParts.multiFileClassParts.isEmpty()) { assertThat(relocatedParts.multiFileClassParts).isEmpty() } else { assertThat(relocatedParts.multiFileClassParts).isNotEmpty() - assertThat(relocatedParts.multiFileClassParts).isNotEqualTo(originalParts.multiFileClassParts) - assertThat(relocatedParts.multiFileClassParts).isEqualTo( - originalParts.multiFileClassParts.entries.associateTo(mutableMapOf()) { (name, facade) -> - name.replace("kotlin/", "my/kotlin/") to - facade.replace("kotlin/", "my/kotlin/") - }, - ) + assertThat(relocatedParts.multiFileClassParts) + .isNotEqualTo(originalParts.multiFileClassParts) + assertThat(relocatedParts.multiFileClassParts) + .isEqualTo( + originalParts.multiFileClassParts.entries.associateTo(mutableMapOf()) { (name, facade) + -> + name.replace("kotlin/", "my/kotlin/") to facade.replace("kotlin/", "my/kotlin/") + } + ) } } } @@ -697,36 +686,39 @@ class RelocationTest : BasePluginTest() { private fun writeClassWithStringRef() { writeClass { """ - package my; - public class Main { - public static void main(String[] args) { - switch (1) { - default: - System.out.println("foo.Foo"); // Test case for string constants used in switch statements. - break; - } - System.out.println("foo.Bar"); + package my; + public class Main { + public static void main(String[] args) { + switch (1) { + default: + System.out.println("foo.Foo"); // Test case for string constants used in switch statements. + break; } + System.out.println("foo.Bar"); } - """.trimIndent() + } + """ + .trimIndent() } } private companion object { @JvmStatic - fun preserveLastModifiedProvider() = listOf( - Arguments.of(false, false), - Arguments.of(true, false), - Arguments.of(false, true), - Arguments.of(true, true), - ) + fun preserveLastModifiedProvider() = + listOf( + Arguments.of(false, false), + Arguments.of(true, false), + Arguments.of(false, true), + Arguments.of(true, true), + ) @JvmStatic - fun relocationCliOptionProvider() = listOf( - Arguments.of(false, "foo"), - Arguments.of(false, "bar"), - Arguments.of(true, "foo"), - Arguments.of(true, "bar"), - ) + fun relocationCliOptionProvider() = + listOf( + Arguments.of(false, "foo"), + Arguments.of(false, "bar"), + Arguments.of(true, "foo"), + Arguments.of(true, "bar"), + ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 8d417f309..05d179e78 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -11,29 +11,29 @@ class AppendingTransformerTest : BaseTransformerTest() { @ParameterizedTest @ValueSource(booleans = [false, true]) fun appendTestProperties(shortSyntax: Boolean) { - val one = buildJarOne { - insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) - } - val two = buildJarTwo { - insert(ENTRY_TEST_PROPERTIES, CONTENT_TWO) - } - val config = if (shortSyntax) { - """ + val one = buildJarOne { insert(ENTRY_TEST_PROPERTIES, CONTENT_ONE) } + val two = buildJarTwo { insert(ENTRY_TEST_PROPERTIES, CONTENT_TWO) } + val config = + if (shortSyntax) { + """ dependencies { ${implementationFiles(one, two)} } $shadowJarTask { append('$ENTRY_TEST_PROPERTIES') } - """.trimIndent() - } else { - transform( - dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + """ + .trimIndent() + } else { + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = + """ resource = '$ENTRY_TEST_PROPERTIES' - """.trimIndent(), - ) - } + """ + .trimIndent(), + ) + } projectScript.appendText(config) runWithSuccess(shadowJarPath) @@ -53,8 +53,9 @@ class AppendingTransformerTest : BaseTransformerTest() { insert("resources/$APPLICATION_YML_FILE", CONTENT_TWO) insert("resources/config/$APPLICATION_YML_FILE", CONTENT_THREE) } - val config = if (shortSyntax) { - """ + val config = + if (shortSyntax) { + """ dependencies { ${implementationFiles(one, two)} } @@ -62,45 +63,56 @@ class AppendingTransformerTest : BaseTransformerTest() { append('resources/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') append('resources/config/$APPLICATION_YML_FILE', '$APPLICATION_YML_SEPARATOR') } - """.trimIndent() - } else { - val block1 = transform( - dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + """ + .trimIndent() + } else { + val block1 = + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = + """ resource = 'resources/$APPLICATION_YML_FILE' separator = '$APPLICATION_YML_SEPARATOR' - """.trimIndent(), - ) - val block2 = transform( - dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + """ + .trimIndent(), + ) + val block2 = + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = + """ resource = 'resources/config/$APPLICATION_YML_FILE' separator = '$APPLICATION_YML_SEPARATOR' - """.trimIndent(), - ) - block1 + lineSeparator + block2 - } + """ + .trimIndent(), + ) + block1 + lineSeparator + block2 + } projectScript.appendText(config) runWithSuccess(shadowJarPath) val content1 = outputShadowedJar.use { it.getContent("resources/$APPLICATION_YML_FILE") } - assertThat(content1).isEqualTo( - """ + assertThat(content1) + .isEqualTo( + """ $CONTENT_ONE --- $CONTENT_TWO - """.trimIndent(), - ) - val content2 = outputShadowedJar.use { it.getContent("resources/config/$APPLICATION_YML_FILE") } - assertThat(content2).isEqualTo( """ + .trimIndent() + ) + val content2 = outputShadowedJar.use { it.getContent("resources/config/$APPLICATION_YML_FILE") } + assertThat(content2) + .isEqualTo( + """ $CONTENT_TWO --- $CONTENT_THREE - """.trimIndent(), - ) + """ + .trimIndent() + ) } private companion object { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 75e6881b1..46d2e8eb8 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -16,7 +16,8 @@ abstract class BaseTransformerTest : BasePluginTest() { // Most transformers in tests require this to handle duplicate resources. duplicatesStrategy = DuplicatesStrategy.INCLUDE } - """.trimIndent() + lineSeparator, + """ + .trimIndent() + lineSeparator ) } @@ -24,7 +25,7 @@ abstract class BaseTransformerTest : BasePluginTest() { builder: JarBuilder.() -> Unit = { insert(ENTRY_SERVICES_SHADE, CONTENT_ONE) insert(ENTRY_SERVICES_FOO, "one") - }, + } ): Path { return buildJar("one.jar", builder) } @@ -33,7 +34,7 @@ abstract class BaseTransformerTest : BasePluginTest() { builder: JarBuilder.() -> Unit = { insert(ENTRY_SERVICES_SHADE, CONTENT_TWO) insert(ENTRY_SERVICES_FOO, "two") - }, + } ): Path { return buildJar("two.jar", builder) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt index 4dbbaf736..ff0c20e6c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformerTest.kt @@ -24,20 +24,22 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { @ParameterizedTest @ValueSource(booleans = [false, true]) fun groovyExtensionModuleTransformer(shortSyntax: Boolean) { - val config = if (shortSyntax) { - """ + val config = + if (shortSyntax) { + """ dependencies { ${implementationFiles(buildJarFoo(), buildJarBar())} } $shadowJarTask { mergeGroovyExtensionModules() } - """.trimIndent() - } else { - transform( - dependenciesBlock = implementationFiles(buildJarFoo(), buildJarBar()), - ) - } + """ + .trimIndent() + } else { + transform( + dependenciesBlock = implementationFiles(buildJarFoo(), buildJarBar()) + ) + } projectScript.appendText(config) runWithSuccess(shadowJarPath) @@ -53,11 +55,8 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { ) { projectScript.appendText( transform( - dependenciesBlock = implementationFiles( - buildJarFoo(fooEntry), - buildJarBar(barEntry), - ), - ), + dependenciesBlock = implementationFiles(buildJarFoo(fooEntry), buildJarBar(barEntry)) + ) ) runWithSuccess(shadowJarPath) @@ -76,7 +75,8 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { relocate('com.acme', 'com.example.shaded.acme') mergeGroovyExtensionModules() } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -88,39 +88,41 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { assertThat(properties.getProperty(KEY_EXTENSION_CLASSES)) .isEqualTo( "com.example.shaded.acme.foo.FooExtension,com.example.shaded.acme.foo.BarExtension," + - "com.example.shaded.acme.bar.SomeExtension,com.example.shaded.acme.bar.AnotherExtension", + "com.example.shaded.acme.bar.SomeExtension,com.example.shaded.acme.bar.AnotherExtension" ) assertThat(properties.getProperty(KEY_STATIC_EXTENSION_CLASSES)) - .isEqualTo("com.example.shaded.acme.foo.FooStaticExtension,com.example.shaded.acme.bar.SomeStaticExtension") + .isEqualTo( + "com.example.shaded.acme.foo.FooStaticExtension,com.example.shaded.acme.bar.SomeStaticExtension" + ) } - private fun buildJarFoo( - entry: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, - ): Path = buildJar("foo.jar") { - insert( - entry, - """ + private fun buildJarFoo(entry: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR): Path = + buildJar("foo.jar") { + insert( + entry, + """ $KEY_MODULE_NAME=foo $KEY_MODULE_VERSION=1.0.5 $KEY_EXTENSION_CLASSES=$EXTENSION_CLASSES_FOO $KEY_STATIC_EXTENSION_CLASSES=$STATIC_EXTENSION_CLASSES_FOO - """.trimIndent(), - ) - } - - private fun buildJarBar( - entry: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, - ): Path = buildJar("bar.jar") { - insert( - entry, """ + .trimIndent(), + ) + } + + private fun buildJarBar(entry: String = PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR): Path = + buildJar("bar.jar") { + insert( + entry, + """ $KEY_MODULE_NAME=bar $KEY_MODULE_VERSION=2.3.5 $KEY_EXTENSION_CLASSES=$EXTENSION_CLASSES_BAR $KEY_STATIC_EXTENSION_CLASSES=$STATIC_EXTENSION_CLASSES_BAR - """.trimIndent(), - ) - } + """ + .trimIndent(), + ) + } private fun commonAssertions() { val properties = outputShadowedJar.extensionModuleProperties @@ -139,16 +141,28 @@ class GroovyExtensionModuleTransformerTest : BaseTransformerTest() { const val STATIC_EXTENSION_CLASSES_FOO = "com.acme.foo.FooStaticExtension" const val STATIC_EXTENSION_CLASSES_BAR = "com.acme.bar.SomeStaticExtension" - val JarPath.extensionModuleProperties get() = use { - it.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR).toProperties() - } + val JarPath.extensionModuleProperties + get() = use { it.getContent(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR).toProperties() } @JvmStatic - fun resourcePathProvider() = listOf( - Arguments.of(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), - Arguments.of(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR), - Arguments.of(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR), - Arguments.of(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), - ) + fun resourcePathProvider() = + listOf( + Arguments.of( + PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + ), + Arguments.of( + PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + ), + Arguments.of( + PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + ), + Arguments.of( + PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR, + ), + ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index 45f3287a3..1df22797d 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -19,61 +19,62 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { @ParameterizedTest @EnumSource(MergeStrategy::class) fun mergePropertiesWithDifferentStrategies(strategy: MergeStrategy) { - val one = buildJarOne { - insert("META-INF/test.properties", "key1=one\nkey2=one") - } - val two = buildJarTwo { - insert("META-INF/test.properties", "key2=two\nkey3=two") - } + val one = buildJarOne { insert("META-INF/test.properties", "key1=one\nkey2=one") } + val two = buildJarTwo { insert("META-INF/test.properties", "key2=two\nkey3=two") } projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + transformerBlock = + """ mergeStrategy = $mergeStrategyClassName.$strategy mergeSeparator = ";" paths = ["META-INF/test.properties"] - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) if (strategy == MergeStrategy.Fail) { val result = runWithFailure(shadowJarPath) assertThat(result).taskOutcomeEquals(shadowJarPath, FAILED) - assertThat(result.output.invariantEolString).contains( - """ + assertThat(result.output.invariantEolString) + .contains( + """ Caused by: java.lang.IllegalStateException: The following properties files have conflicting property values and cannot be merged: * META-INF/test.properties * Property key2 is duplicated 2 times with different values - """.trimIndent(), - ) + """ + .trimIndent() + ) } else { runWithSuccess(shadowJarPath) - val expected = when (strategy) { - MergeStrategy.First -> - """ - |key1=one - |key2=one - |key3=two - | - """.trimMargin() - MergeStrategy.Latest -> - """ - |key1=one - |key2=two - |key3=two - | - """.trimMargin() - MergeStrategy.Append -> - """ - |key1=one - |key2=one;two - |key3=two - | - """.trimMargin() - else -> fail("Unexpected strategy: $strategy") - } + val expected = + when (strategy) { + MergeStrategy.First -> + """ + |key1=one + |key2=one + |key3=two + |""" + .trimMargin() + MergeStrategy.Latest -> + """ + |key1=one + |key2=two + |key3=two + |""" + .trimMargin() + MergeStrategy.Append -> + """ + |key1=one + |key2=one;two + |key3=two + |""" + .trimMargin() + else -> fail("Unexpected strategy: $strategy") + } val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } assertThat(content.invariantEolString).isEqualTo(expected) } @@ -81,21 +82,19 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { @Test fun mergePropertiesWithKeyTransformer() { - val one = buildJarOne { - insert("META-INF/test.properties", "foo=bar") - } - val two = buildJarTwo { - insert("META-INF/test.properties", "FOO=baz") - } + val one = buildJarOne { insert("META-INF/test.properties", "foo=bar") } + val two = buildJarTwo { insert("META-INF/test.properties", "FOO=baz") } projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + transformerBlock = + """ mergeStrategy = $mergeStrategyClassName.Append keyTransformer = { key -> key.toUpperCase() } paths = ["META-INF/test.properties"] - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) runWithSuccess(shadowJarPath) @@ -106,21 +105,19 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { @Test fun mergePropertiesWithSpecifiedCharset() { - val one = buildJarOne { - insert("META-INF/utf8.properties", "foo=第一") - } - val two = buildJarTwo { - insert("META-INF/utf8.properties", "foo=第二") - } + val one = buildJarOne { insert("META-INF/utf8.properties", "foo=第一") } + val two = buildJarTwo { insert("META-INF/utf8.properties", "foo=第二") } projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + transformerBlock = + """ mergeStrategy = $mergeStrategyClassName.Append charsetName = "utf-8" paths = ["META-INF/utf8.properties"] - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) runWithSuccess(shadowJarPath) @@ -142,13 +139,15 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + transformerBlock = + """ mappings = [ "META-INF/foo.properties": ["mergeStrategy": "append", "mergeSeparator": ";"], "META-INF/bar.properties": ["mergeStrategy": "latest"] ] - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) runWithSuccess(shadowJarPath) @@ -159,48 +158,51 @@ class PropertiesFileTransformerTest : BaseTransformerTest() { } } - @Issue( - "https://github.com/GradleUp/shadow/issues/856", - ) + @Issue("https://github.com/GradleUp/shadow/issues/856") @Test fun mergedPropertiesWithoutComments() { val one = buildJarOne { insert( "META-INF/test.properties", """ - # A comment from jar one. - foo=one - """.trimIndent(), + # A comment from jar one. + foo=one + """ + .trimIndent(), ) } val two = buildJarTwo { insert( "META-INF/test.properties", """ - # A comment from jar two. - foo=two - """.trimIndent(), + # A comment from jar two. + foo=two + """ + .trimIndent(), ) } projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + transformerBlock = + """ mergeStrategy = $mergeStrategyClassName.Append paths = ["META-INF/test.properties"] - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent("META-INF/test.properties") } - assertThat(content.invariantEolString).isEqualTo( - """ + assertThat(content.invariantEolString) + .isEqualTo( + """ |foo=one,two - | - """.trimMargin(), - ) + |""" + .trimMargin() + ) } private companion object { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index 6162f3f3d..f1902d590 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -23,8 +23,9 @@ class ServiceFileTransformerTest : BaseTransformerTest() { @ParameterizedTest @ValueSource(booleans = [false, true]) fun serviceResourceTransformer(shortSyntax: Boolean) { - val config = if (shortSyntax) { - """ + val config = + if (shortSyntax) { + """ dependencies { ${implementationFiles(buildJarOne(), buildJarTwo())} } @@ -33,15 +34,18 @@ class ServiceFileTransformerTest : BaseTransformerTest() { exclude 'META-INF/services/com.acme.*' } } - """.trimIndent() - } else { - transform( - dependenciesBlock = implementationFiles(buildJarOne(), buildJarTwo()), - transformerBlock = """ - exclude 'META-INF/services/com.acme.*' - """.trimIndent(), - ) - } + """ + .trimIndent() + } else { + transform( + dependenciesBlock = implementationFiles(buildJarOne(), buildJarTwo()), + transformerBlock = + """ + exclude 'META-INF/services/com.acme.*' + """ + .trimIndent(), + ) + } projectScript.appendText(config) runWithSuccess(shadowJarPath) @@ -55,29 +59,29 @@ class ServiceFileTransformerTest : BaseTransformerTest() { @ParameterizedTest @ValueSource(booleans = [false, true]) fun serviceResourceTransformerAlternatePath(shortSyntax: Boolean) { - val one = buildJarOne { - insert(ENTRY_FOO_SHADE, CONTENT_ONE) - } - val two = buildJarTwo { - insert(ENTRY_FOO_SHADE, CONTENT_TWO) - } - val config = if (shortSyntax) { - """ + val one = buildJarOne { insert(ENTRY_FOO_SHADE, CONTENT_ONE) } + val two = buildJarTwo { insert(ENTRY_FOO_SHADE, CONTENT_TWO) } + val config = + if (shortSyntax) { + """ dependencies { ${implementationFiles(one, two)} } $shadowJarTask { mergeServiceFiles("META-INF/foo") } - """.trimIndent() - } else { - transform( - dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ - path = 'META-INF/foo' - """.trimIndent(), - ) - } + """ + .trimIndent() + } else { + transform( + dependenciesBlock = implementationFiles(one, two), + transformerBlock = + """ + path = 'META-INF/foo' + """ + .trimIndent(), + ) + } projectScript.appendText(config) runWithSuccess(shadowJarPath) @@ -92,9 +96,10 @@ class ServiceFileTransformerTest : BaseTransformerTest() { insert( "META-INF/services/java.sql.Driver", """ - oracle.jdbc.OracleDriver - org.apache.hive.jdbc.HiveDriver - """.trimIndent(), + oracle.jdbc.OracleDriver + org.apache.hive.jdbc.HiveDriver + """ + .trimIndent(), ) insert( "META-INF/services/org.apache.axis.components.compiler.Compiler", @@ -109,18 +114,16 @@ class ServiceFileTransformerTest : BaseTransformerTest() { insert( "META-INF/services/java.sql.Driver", """ - org.apache.derby.jdbc.AutoloadedDriver - com.mysql.jdbc.Driver - """.trimIndent(), + org.apache.derby.jdbc.AutoloadedDriver + com.mysql.jdbc.Driver + """ + .trimIndent(), ) insert( "META-INF/services/org.apache.axis.components.compiler.Compiler", "org.apache.axis.components.compiler.Jikes", ) - insert( - "META-INF/services/org.apache.commons.logging.LogFactory", - "org.mortbay.log.Factory", - ) + insert("META-INF/services/org.apache.commons.logging.LogFactory", "org.mortbay.log.Factory") } projectScript.appendText( @@ -135,32 +138,39 @@ class ServiceFileTransformerTest : BaseTransformerTest() { exclude 'org.apache.commons.logging.LogFactory' } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - getContent("META-INF/services/java.sql.Driver").isEqualTo( - """ + getContent("META-INF/services/java.sql.Driver") + .isEqualTo( + """ oracle.jdbc.OracleDriver myapache.hive.jdbc.HiveDriver myapache.derby.jdbc.AutoloadedDriver com.mysql.jdbc.Driver - """.trimIndent(), - ) - getContent("META-INF/services/myapache.axis.components.compiler.Compiler").isEqualTo( - """ + """ + .trimIndent() + ) + getContent("META-INF/services/myapache.axis.components.compiler.Compiler") + .isEqualTo( + """ myapache.axis.components.compiler.Javac org.apache.axis.components.compiler.Jikes - """.trimIndent(), - ) - getContent("META-INF/services/org.apache.commons.logging.LogFactory").isEqualTo( - """ + """ + .trimIndent() + ) + getContent("META-INF/services/org.apache.commons.logging.LogFactory") + .isEqualTo( + """ myapache.commons.logging.impl.LogFactoryImpl org.mortbay.log.Factory - """.trimIndent(), - ) + """ + .trimIndent() + ) } } @@ -171,12 +181,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { @Test fun transformProjectResources() { val servicesBarEntry = "META-INF/services/foo.Bar" - val one = buildJarOne { - insert(servicesBarEntry, CONTENT_ONE) - } - val two = buildJarTwo { - insert(servicesBarEntry, CONTENT_TWO) - } + val one = buildJarOne { insert(servicesBarEntry, CONTENT_ONE) } + val two = buildJarTwo { insert(servicesBarEntry, CONTENT_TWO) } projectScript.appendText( """ dependencies { @@ -185,7 +191,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { $shadowJarTask { mergeServiceFiles() } - """.trimIndent(), + """ + .trimIndent() ) path("src/main/resources/$servicesBarEntry").writeText(CONTENT_THREE) @@ -197,10 +204,7 @@ class ServiceFileTransformerTest : BaseTransformerTest() { @ParameterizedTest @MethodSource("withThrowingProvider") - fun honorDuplicatesStrategyWithThrowing( - strategy: DuplicatesStrategy, - outputRegex: String, - ) { + fun honorDuplicatesStrategyWithThrowing(strategy: DuplicatesStrategy, outputRegex: String) { writeDuplicatesStrategy(strategy) val result = runWithFailure(shadowJarPath) @@ -235,7 +239,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { duplicatesStrategy = DuplicatesStrategy.INCLUDE } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -256,7 +261,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -284,7 +290,8 @@ class ServiceFileTransformerTest : BaseTransformerTest() { } } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -305,28 +312,38 @@ class ServiceFileTransformerTest : BaseTransformerTest() { duplicatesStrategy = DuplicatesStrategy.$strategy mergeServiceFiles() } - """.trimIndent() + lineSeparator, + """ + .trimIndent() + lineSeparator ) } private companion object { @JvmStatic - fun withThrowingProvider() = listOf( - Arguments.of(FAIL, "Cannot copy zip entry .* to .* because zip entry .* has already been copied there"), - Arguments.of(INHERIT, "Entry .* is a duplicate but no duplicate handling strategy has been set"), - ) + fun withThrowingProvider() = + listOf( + Arguments.of( + FAIL, + "Cannot copy zip entry .* to .* because zip entry .* has already been copied there", + ), + Arguments.of( + INHERIT, + "Entry .* is a duplicate but no duplicate handling strategy has been set", + ), + ) @JvmStatic - fun withoutThrowingProvider() = listOf( - Arguments.of(EXCLUDE, CONTENT_ONE, "one"), - Arguments.of(INCLUDE, CONTENT_ONE_TWO, "one\ntwo"), - Arguments.of(WARN, CONTENT_ONE_TWO, "one\ntwo"), - ) + fun withoutThrowingProvider() = + listOf( + Arguments.of(EXCLUDE, CONTENT_ONE, "one"), + Arguments.of(INCLUDE, CONTENT_ONE_TWO, "one\ntwo"), + Arguments.of(WARN, CONTENT_ONE_TWO, "one\ntwo"), + ) @JvmStatic - fun eachFileStrategyProvider() = listOf( - Arguments.of(EXCLUDE, INCLUDE, ENTRY_SERVICES_SHADE), - Arguments.of(INCLUDE, EXCLUDE, ENTRY_SERVICES_FOO), - ) + fun eachFileStrategyProvider() = + listOf( + Arguments.of(EXCLUDE, INCLUDE, ENTRY_SERVICES_SHADE), + Arguments.of(INCLUDE, EXCLUDE, ENTRY_SERVICES_FOO), + ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt index 90e0db8c5..540a7c2d5 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformersTest.kt @@ -50,11 +50,13 @@ class TransformersTest : BaseTransformerTest() { projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + transformerBlock = + """ exclude('multiple-contents') ${if (excludeAll) "exclude('differing-content-2')" else ""} - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) if (excludeAll) { @@ -72,22 +74,25 @@ class TransformersTest : BaseTransformerTest() { "META-INF/", "META-INF/MANIFEST.MF", ) - getContents("multiple-contents").containsExactlyInAnyOrder("content", "content-is-different") + getContents("multiple-contents") + .containsExactlyInAnyOrder("content", "content-is-different") getContent("single-source").isEqualTo("content") getContent("same-content-twice").isEqualTo("content") - getContents("differing-content-2").containsExactlyInAnyOrder("content", "content-is-different") + getContents("differing-content-2") + .containsExactlyInAnyOrder("content", "content-is-different") } } else { val buildResult = runWithFailure(shadowJarPath) assertThat(buildResult).taskOutcomeEquals(shadowJarPath, FAILED) - assertThat(buildResult.output).contains( - // Keep this list approach for Unix/Windows test compatibility. - "Execution failed for task ':shadowJar'.", - "> Found 1 path duplicate(s) with different content in the shadowed JAR:", - " * differing-content-2", - "differing-content-2 (SHA256: ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73)", - "differing-content-2 (SHA256: aa845861bbd4578700e10487d85b25ead8723ee98fbf143df7b7e0bf1cb3385d)", - ) + assertThat(buildResult.output) + .contains( + // Keep this list approach for Unix/Windows test compatibility. + "Execution failed for task ':shadowJar'.", + "> Found 1 path duplicate(s) with different content in the shadowed JAR:", + " * differing-content-2", + "differing-content-2 (SHA256: ed7002b439e9ac845f22357d822bac1444730fbdb6016d3ec9432297b9ec9f73)", + "differing-content-2 (SHA256: aa845861bbd4578700e10487d85b25ead8723ee98fbf143df7b7e0bf1cb3385d)", + ) } } @@ -102,7 +107,8 @@ class TransformersTest : BaseTransformerTest() { attributes '$TEST_ENTRY_ATTR_KEY': 'PASSED' } } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) @@ -124,29 +130,22 @@ class TransformersTest : BaseTransformerTest() { commonAssertions() } - @Issue( - "https://github.com/GradleUp/shadow/issues/427", - ) + @Issue("https://github.com/GradleUp/shadow/issues/427") @Test fun mergeLog4j2PluginCacheFiles() { val content = requireResourceAsPath(PLUGIN_CACHE_FILE).readText() - val one = buildJarOne { - insert(PLUGIN_CACHE_FILE, content) - } - val two = buildJarOne { - insert(PLUGIN_CACHE_FILE, content) - } + val one = buildJarOne { insert(PLUGIN_CACHE_FILE, content) } + val two = buildJarOne { insert(PLUGIN_CACHE_FILE, content) } projectScript.appendText( transform( - dependenciesBlock = implementationFiles(one, two), - ), + dependenciesBlock = implementationFiles(one, two) + ) ) runWithSuccess(shadowJarPath) - val actualFileBytes = outputShadowedJar.use { jar -> - jar.getStream(PLUGIN_CACHE_FILE).use { it.readAllBytes() } - } + val actualFileBytes = + outputShadowedJar.use { jar -> jar.getStream(PLUGIN_CACHE_FILE).use { it.readAllBytes() } } assertThat(actualFileBytes.contentHashCode()).all { // Hash of the original plugin cache file. isNotEqualTo(-2114104185) @@ -159,20 +158,19 @@ class TransformersTest : BaseTransformerTest() { val foo = path("foo").apply { writeText("foo") } projectScript.appendText( transform( - transformerBlock = """ + transformerBlock = + """ resource = 'bar' file = file('${foo.invariantSeparatorsPathString}') - """.trimIndent(), - ), + """ + .trimIndent() + ) ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "bar", - *manifestEntries, - ) + containsOnly("bar", *manifestEntries) getContent("bar").isEqualTo("foo") } } @@ -187,16 +185,13 @@ class TransformersTest : BaseTransformerTest() { transform( dependenciesBlock = implementationFiles(one), transformerBlock = "resource = 'foo'", - ), + ) ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "bar", - *manifestEntries, - ) + containsOnly("bar", *manifestEntries) getContent("bar").isEqualTo("foo") } } @@ -217,18 +212,13 @@ class TransformersTest : BaseTransformerTest() { transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = "resources = ['foo/bar']", - ), + ) ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "foo/", - "foo/bar", - "foo/baz", - *manifestEntries, - ) + containsOnly("foo/", "foo/bar", "foo/baz", *manifestEntries) getContent("foo/bar").isEqualTo("bar1") getContent("foo/baz").isEqualTo("baz3") } @@ -246,22 +236,16 @@ class TransformersTest : BaseTransformerTest() { // Use Transformer.Companion (no-op) to mock a custom transformer here. transform(${ResourceTransformer.Companion::class.java.name}) } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) - assertThat(outputShadowedJar).useAll { - containsOnly( - *entriesInAB, - *manifestEntries, - ) - } + assertThat(outputShadowedJar).useAll { containsOnly(*entriesInAB, *manifestEntries) } } - @Issue( - "https://github.com/GradleUp/shadow/issues/1626", - ) + @Issue("https://github.com/GradleUp/shadow/issues/1626") @Test fun useApacheNoticeTransformerWithoutProjectName() { val noticeEntry = "META-INF/NOTICE" @@ -274,7 +258,8 @@ class TransformersTest : BaseTransformerTest() { This product includes software developed at The Apache Software Foundation (https://www.apache.org/). - """.trimIndent(), + """ + .trimIndent(), ) } val two = buildJarTwo { @@ -286,35 +271,35 @@ class TransformersTest : BaseTransformerTest() { This product includes software developed at The Apache Software Foundation (https://www.apache.org/). - """.trimIndent(), + """ + .trimIndent(), ) } projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = "addHeader = false", - ), + ) ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - noticeEntry, - *manifestEntries, - ) - getContent(noticeEntry).isEqualTo( - """ - Apache Commons Pool - Copyright 2001-2025 The Apache Software Foundation + containsOnly(noticeEntry, *manifestEntries) + getContent(noticeEntry) + .isEqualTo( + """ + Apache Commons Pool + Copyright 2001-2025 The Apache Software Foundation - This product includes software developed at - The Apache Software Foundation (https://www.apache.org/). + This product includes software developed at + The Apache Software Foundation (https://www.apache.org/). - Apache Commons DBCP - Copyright 2001-2024 The Apache Software Foundation - """.trimIndent(), - ) + Apache Commons DBCP + Copyright 2001-2024 The Apache Software Foundation + """ + .trimIndent() + ) } } @@ -322,28 +307,22 @@ class TransformersTest : BaseTransformerTest() { fun overrideOutputPathOfNoticeFile() { val noticeEntry = "META-INF/NOTICE" val customNoticeEntry = "META-INF/CUSTOM_NOTICE" - val one = buildJarOne { - insert(noticeEntry, "Notice from A") - } - val two = buildJarTwo { - insert(noticeEntry, "Notice from B") - } + val one = buildJarOne { insert(noticeEntry, "Notice from A") } + val two = buildJarTwo { insert(noticeEntry, "Notice from B") } projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), transformerBlock = "addHeader = false; outputPath = '$customNoticeEntry'", - ), + ) ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - customNoticeEntry, - *manifestEntries, - ) - getContent(customNoticeEntry).isEqualTo( - """ + containsOnly(customNoticeEntry, *manifestEntries) + getContent(customNoticeEntry) + .isEqualTo( + """ Copyright 2006-2026 The Apache Software Foundation This product includes software developed at @@ -352,8 +331,9 @@ class TransformersTest : BaseTransformerTest() { Notice from A Notice from B - """.trimIndent(), - ) + """ + .trimIndent() + ) } } @@ -370,14 +350,13 @@ class TransformersTest : BaseTransformerTest() { $shadowJarTask { transform(${transformer.java.name}) $configuration } - """.trimIndent(), + """ + .trimIndent() ) runWithSuccess(shadowJarPath) - assertThat(outputShadowedJar).useAll { - containsAtLeast(*entriesInAB) - } + assertThat(outputShadowedJar).useAll { containsAtLeast(*entriesInAB) } } private fun commonAssertions( @@ -385,7 +364,7 @@ class TransformersTest : BaseTransformerTest() { assertThat(getValue(TEST_ENTRY_ATTR_KEY)).isEqualTo("PASSED") assertThat(getValue(mainClassAttributeKey)).isEqualTo("my.Main") assertThat(getValue(NEW_ENTRY_ATTR_KEY)).isEqualTo("NEW") - }, + } ) { val mf = outputShadowedJar.use { it.manifest } assertThat(mf).isNotNull() @@ -396,7 +375,8 @@ class TransformersTest : BaseTransformerTest() { const val NEW_ENTRY_ATTR_KEY = "New-Entry" const val TEST_ENTRY_ATTR_KEY = "Test-Entry" - val MANIFEST_ATTRS = """ + val MANIFEST_ATTRS = + """ $jarTask { manifest { attributes '$mainClassAttributeKey': 'my.Main' @@ -409,58 +389,57 @@ class TransformersTest : BaseTransformerTest() { attributes '$TEST_ENTRY_ATTR_KEY': 'PASSED' } } - """.trimIndent() + """ + .trimIndent() @JvmStatic - fun transformerConfigProvider() = listOf( - "" to ApacheLicenseResourceTransformer::class, - "" to ComponentsXmlResourceTransformer::class, - "" to ManifestAppenderTransformer::class, - "" to ManifestResourceTransformer::class, - ) + fun transformerConfigProvider() = + listOf( + "" to ApacheLicenseResourceTransformer::class, + "" to ComponentsXmlResourceTransformer::class, + "" to ManifestAppenderTransformer::class, + "" to ManifestResourceTransformer::class, + ) } @Test fun mergeLicenseResourceTransformer() { - val one = buildJarOne { - insert("META-INF/LICENSE", "license one") - } - val two = buildJarTwo { - insert("META-INF/LICENSE", "license two") - } + val one = buildJarOne { insert("META-INF/LICENSE", "license one") } + val two = buildJarTwo { insert("META-INF/LICENSE", "license two") } val artifactLicense = path("my-license") artifactLicense.writeText("artifact license text") projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + transformerBlock = + """ outputPath = 'MY_LICENSE' artifactLicense = file('${artifactLicense.invariantSeparatorsPathString}') firstSeparator = '####' separator = '----' - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) runWithSuccess(shadowJarPath) assertThat(outputShadowedJar).useAll { - containsOnly( - "MY_LICENSE", - "META-INF/", - "META-INF/MANIFEST.MF", - ) - getContent("MY_LICENSE").transform { it.invariantEolString }.isEqualTo( - """ + containsOnly("MY_LICENSE", "META-INF/", "META-INF/MANIFEST.MF") + getContent("MY_LICENSE") + .transform { it.invariantEolString } + .isEqualTo( + """ SPDX-License-Identifier: Apache-2.0 artifact license text #### license one ---- license two - """.trimIndent(), - ) + """ + .trimIndent() + ) } } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt index 2cc226f20..3721f9a89 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformerTest.kt @@ -11,81 +11,83 @@ class XmlAppendingTransformerTest : BaseTransformerTest() { @Test fun appendXmlFiles() { val xmlEntry = "properties.xml" - val xmlContent = """ + val xmlContent = + """ %s - """.trimIndent() - val one = buildJarOne { - insert(xmlEntry, xmlContent.format("key1", "val1")) - } - val two = buildJarTwo { - insert(xmlEntry, xmlContent.format("key2", "val2")) - } + """ + .trimIndent() + val one = buildJarOne { insert(xmlEntry, xmlContent.format("key1", "val1")) } + val two = buildJarTwo { insert(xmlEntry, xmlContent.format("key2", "val2")) } projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + transformerBlock = + """ resource = '$xmlEntry' - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent(xmlEntry) }.trimIndent() - assertThat(content).isEqualTo( - """ + assertThat(content) + .isEqualTo( + """ val1 val2 - """.trimIndent(), - ) + """ + .trimIndent() + ) } - @Issue( - "https://github.com/GradleUp/shadow/issues/168", - ) + @Issue("https://github.com/GradleUp/shadow/issues/168") @Test fun mergeNestedLevels() { val xmlEntry = "META-INF/nested.xml" - val xmlContent = """ + val xmlContent = + """ %s - """.trimIndent() - val one = buildJarOne { - insert(xmlEntry, xmlContent.format("")) - } - val two = buildJarTwo { - insert(xmlEntry, xmlContent.format("")) - } + """ + .trimIndent() + val one = buildJarOne { insert(xmlEntry, xmlContent.format("")) } + val two = buildJarTwo { insert(xmlEntry, xmlContent.format("")) } projectScript.appendText( transform( dependenciesBlock = implementationFiles(one, two), - transformerBlock = """ + transformerBlock = + """ resource = '$xmlEntry' - """.trimIndent(), - ), + """ + .trimIndent(), + ) ) runWithSuccess(shadowJarPath) val content = outputShadowedJar.use { it.getContent(xmlEntry) }.trimIndent() - assertThat(content).isEqualTo( - """ + assertThat(content) + .isEqualTo( + """ - """.trimIndent(), - ) + """ + .trimIndent() + ) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt index cda9c70c0..629aba91c 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/AppendableMavenRepository.kt @@ -16,16 +16,16 @@ import org.apache.maven.model.Dependency import org.apache.maven.model.Model import org.gradle.api.logging.Logging -class AppendableMavenRepository( - val root: Path, -) { +class AppendableMavenRepository(val root: Path) { private val modules = mutableListOf() private val jarsDir: Path init { check(root.exists()) { "Maven repository root directory does not exist: $root" } - root.resolve("settings.gradle").createFile() + root + .resolve("settings.gradle") + .createFile() .writeText("rootProject.name = '${root.name}'$lineSeparator") root.resolve("build.gradle").createFile() jarsDir = root.resolve("jars").createDirectory() @@ -54,61 +54,64 @@ class AppendableMavenRepository( } fun publish() { - check(modules.isNotEmpty()) { - "No modules to publish. Please add at least one module." - } - modules.groupBy { it::class }.forEach { (type, group) -> - @Suppress("UNCHECKED_CAST") - when (type) { - JarModule::class -> { - configureJarModules(group as List) - } - BomModule::class -> { - configureBomModules(group as List) + check(modules.isNotEmpty()) { "No modules to publish. Please add at least one module." } + modules + .groupBy { it::class } + .forEach { (type, group) -> + @Suppress("UNCHECKED_CAST") + when (type) { + JarModule::class -> { + configureJarModules(group as List) + } + BomModule::class -> { + configureBomModules(group as List) + } + else -> error("Unsupported module type: $type") } - else -> error("Unsupported module type: $type") } - } - gradleRunner( - projectDir = root, - arguments = commonGradleArgs + "publish", - ).build() + gradleRunner(projectDir = root, arguments = commonGradleArgs + "publish").build() logger.info( """ Publish modules to Maven repository at ${root.toUri()}: ${modules.joinToString(lineSeparator) { it.coordinate }} - """.trimIndent(), + """ + .trimIndent() ) modules.clear() } private fun configureJarModules(jarModules: List) { - val mavenPublications = jarModules.joinToString(lineSeparator) { module -> - var index = -1 - val nodes = module.dependencies.joinToString(lineSeparator) { - index++ - val node = "dependencyNode$index" - """ + val mavenPublications = + jarModules.joinToString(lineSeparator) { module -> + var index = -1 + val nodes = + module.dependencies.joinToString(lineSeparator) { + index++ + val node = "dependencyNode$index" + """ def $node = dependenciesNode.appendNode('dependency') $node.appendNode('groupId', '${it.groupId}') $node.appendNode('artifactId', '${it.artifactId}') $node.appendNode('version', '${it.version}') $node.appendNode('scope', '${it.scope}') - """.trimIndent() - } - module.createMavenPublication( """ + .trimIndent() + } + module.createMavenPublication( + """ artifact '${module.artifactPath}' pom.withXml { xml -> def dependenciesNode = xml.asNode().get('dependencies') ?: xml.asNode().appendNode('dependencies') $nodes } - """.trimIndent(), - ) - } - val scriptContent = """ + """ + .trimIndent() + ) + } + val scriptContent = + """ plugins { id 'maven-publish' } @@ -120,18 +123,18 @@ class AppendableMavenRepository( maven { url = '${root.toUri()}' } } } - """.trimIndent() + """ + .trimIndent() val jarsModule = "jars-module" root.resolve("settings.gradle").appendText("include '$jarsModule'$lineSeparator") - root.resolve("$jarsModule/build.gradle") - .createFileIfNotExists() - .writeText(scriptContent) + root.resolve("$jarsModule/build.gradle").createFileIfNotExists().writeText(scriptContent) } private fun configureBomModules(bomModules: List) { // BOM modules are published one by one. bomModules.forEachIndexed { index, module -> - val scriptContent = """ + val scriptContent = + """ plugins { id 'maven-publish' id 'java-platform' @@ -149,18 +152,15 @@ class AppendableMavenRepository( maven { url = '${root.toUri()}' } } } - """.trimIndent() + """ + .trimIndent() val pomModule = "pom-module-$index" root.resolve("settings.gradle").appendText("include '$pomModule'$lineSeparator") - root.resolve("$pomModule/build.gradle") - .createFileIfNotExists() - .writeText(scriptContent) + root.resolve("$pomModule/build.gradle").createFileIfNotExists().writeText(scriptContent) } } - private fun Module.createMavenPublication( - block: String, - ): String { + private fun Module.createMavenPublication(block: String): String { return """ create('${coordinate.replace(":", "")}', MavenPublication) { artifactId = '$artifactId' @@ -168,14 +168,11 @@ class AppendableMavenRepository( version = '$version' $block } - """.trimIndent() + """ + .trimIndent() } - sealed class Module( - groupId: String, - artifactId: String, - version: String, - ) : Model() { + sealed class Module(groupId: String, artifactId: String, version: String) : Model() { val coordinate = "$groupId:$artifactId:$version" init { @@ -185,29 +182,35 @@ class AppendableMavenRepository( } fun addDependency(coordinate: String, scope: String = "runtime") { - val (groupId, artifactId, version) = coordinate.split(":").takeIf { it.size == 3 } - ?: error("Invalid coordinate format: '$coordinate'. Expected format is 'groupId:artifactId:version'.") - val dependency = Dependency().also { - it.groupId = groupId - it.artifactId = artifactId - it.version = version - it.scope = scope - } + val (groupId, artifactId, version) = + coordinate.split(":").takeIf { it.size == 3 } + ?: error( + "Invalid coordinate format: '$coordinate'. Expected format is 'groupId:artifactId:version'." + ) + val dependency = + Dependency().also { + it.groupId = groupId + it.artifactId = artifactId + it.version = version + it.scope = scope + } addDependency(dependency) } } - inner class JarModule( - groupId: String, - artifactId: String, - version: String, - ) : Module(groupId, artifactId, version) { + inner class JarModule(groupId: String, artifactId: String, version: String) : + Module(groupId, artifactId, version) { private var existingJar: Path? = null val artifactPath: String - get() = existingJar?.also { - check(it.exists() && it.isRegularFile()) { "Jar file does not exist or is not a regular file: $it" } - }?.invariantSeparatorsPathString ?: error("No jar file provided for $coordinate") + get() = + existingJar + ?.also { + check(it.exists() && it.isRegularFile()) { + "Jar file does not exist or is not a regular file: $it" + } + } + ?.invariantSeparatorsPathString ?: error("No jar file provided for $coordinate") fun useJar(existingJar: Path) { this.existingJar = existingJar @@ -219,11 +222,8 @@ class AppendableMavenRepository( } } - class BomModule( - groupId: String, - artifactId: String, - version: String, - ) : Module(groupId, artifactId, version) { + class BomModule(groupId: String, artifactId: String, version: String) : + Module(groupId, artifactId, version) { init { packaging = "pom" } @@ -234,7 +234,8 @@ private val logger = Logging.getLogger(AppendableMavenRepository::class.java) private val lineSeparator = System.lineSeparator() -val Dependency.coordinate: String get() = "$groupId:$artifactId:$version" +val Dependency.coordinate: String + get() = "$groupId:$artifactId:$version" private fun Path.createFileIfNotExists(): Path { if (!exists()) { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt index 4ae3ebc55..fef15ab37 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/GradleModuleMetadata.kt @@ -5,16 +5,21 @@ import org.gradle.api.plugins.JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME /** - * Refs the format from [Gradle Module Metadata](https://github.com/gradle/gradle/blob/master/platforms/documentation/docs/src/docs/design/gradle-module-metadata-latest-specification.md). + * Refs the format from + * [Gradle Module Metadata](https://github.com/gradle/gradle/blob/master/platforms/documentation/docs/src/docs/design/gradle-module-metadata-latest-specification.md). */ -data class GradleModuleMetadata( - private val variants: List, -) { - val apiElementsVariant: Variant get() = variants.single { it.name == API_ELEMENTS_CONFIGURATION_NAME } - val runtimeElementsVariant: Variant get() = variants.single { it.name == RUNTIME_ELEMENTS_CONFIGURATION_NAME } - val shadowRuntimeElementsVariant: Variant get() = variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME } +data class GradleModuleMetadata(private val variants: List) { + val apiElementsVariant: Variant + get() = variants.single { it.name == API_ELEMENTS_CONFIGURATION_NAME } - val variantNames: List get() = variants.map { it.name } + val runtimeElementsVariant: Variant + get() = variants.single { it.name == RUNTIME_ELEMENTS_CONFIGURATION_NAME } + + val shadowRuntimeElementsVariant: Variant + get() = variants.single { it.name == SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME } + + val variantNames: List + get() = variants.map { it.name } data class Variant( val name: String, @@ -22,21 +27,19 @@ data class GradleModuleMetadata( private val dependencies: List = emptyList(), private val files: List = emptyList(), ) { - val coordinates: List get() = dependencies.map { it.coordinate } - val fileNames: List get() = files.map { it.name } + val coordinates: List + get() = dependencies.map { it.coordinate } + + val fileNames: List + get() = files.map { it.name } - data class Dependency( - val group: String, - val module: String, - val version: Version, - ) { - val coordinate: String get() = "$group:$module:${version.requires}" + data class Dependency(val group: String, val module: String, val version: Version) { + val coordinate: String + get() = "$group:$module:${version.requires}" data class Version(val requires: String) } - data class File( - val name: String, - ) + data class File(val name: String) } } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt index e7b936358..f0b5c4010 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/Issue.kt @@ -6,7 +6,9 @@ import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.api.extension.ExtensionContext.Namespace /** - * This is related to [spock.lang.Issue](https://github.com/spockframework/spock/blob/master/spock-core/src/main/java/spock/lang/Issue.java) but is used for JUnit 5 tests. + * This is related to + * [spock.lang.Issue](https://github.com/spockframework/spock/blob/master/spock-core/src/main/java/spock/lang/Issue.java) + * but is used for JUnit 5 tests. * * @see [Tags] */ @@ -17,15 +19,14 @@ annotation class Issue(vararg val values: String) class IssueExtension : BeforeEachCallback { override fun beforeEach(context: ExtensionContext) { val issueAnnotation = context.requiredTestMethod.getAnnotation(Issue::class.java) ?: return - val store = context.getStore(Namespace.create(IssueExtension::class.java, context.requiredTestClass)) + val store = + context.getStore(Namespace.create(IssueExtension::class.java, context.requiredTestClass)) store.put("tags", issueAnnotation.values.map(::issueLinkToTag)) } } private fun issueLinkToTag(link: String): String { - return issueLinkRegex.replace(link) { matchResult -> - "ISSUE-${matchResult.groupValues[1]}" - } + return issueLinkRegex.replace(link) { matchResult -> "ISSUE-${matchResult.groupValues[1]}" } } private val issueLinkRegex = "https://github\\.com/[^/]+/[^/]+/issues/(\\d+)".toRegex() diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt index 3fc1eb220..67964a25b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JarBuilder.kt @@ -5,18 +5,14 @@ import java.util.jar.JarEntry import java.util.jar.JarOutputStream import kotlin.io.path.outputStream -class JarBuilder( - private val outputPath: Path, -) { +class JarBuilder(private val outputPath: Path) { private val contents = mutableMapOf() private val entries = mutableSetOf() private val jos = JarOutputStream(outputPath.outputStream()) fun insert(entry: String, content: String): JarBuilder = insert(entry, content.toByteArray()) - fun insert(entry: String, content: ByteArray): JarBuilder = apply { - contents[entry] = content - } + fun insert(entry: String, content: ByteArray): JarBuilder = apply { contents[entry] = content } fun write(): Path { jos.use { diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt index 2243ef674..9beeaf8ac 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/JvmLang.kt @@ -1,11 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.util -enum class JvmLang( - val suffix: String, -) { +enum class JvmLang(val suffix: String) { Java("java"), - Kotlin("kt"), - ; + Kotlin("kt"); override fun toString(): String = name.lowercase() } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt index d9e3f5218..8cbc02816 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/util/RunProcess.kt @@ -1,17 +1,13 @@ package com.github.jengelman.gradle.plugins.shadow.util -fun runProcess( - vararg commands: String, -): String { +fun runProcess(vararg commands: String): String { val process = ProcessBuilder(commands.toList()).start() val exitCode = process.waitFor() val err = process.errorStream.bufferedReader().use { it.readText() } val out = process.inputStream.bufferedReader().use { it.readText() } - check(exitCode == 0 && err.isEmpty()) { - "Error occurred when running command line: $err" - } + check(exitCode == 0 && err.isEmpty()) { "Error occurred when running command line: $err" } return out } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt index 0f1879582..115c7575f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowApplicationPlugin.kt @@ -26,18 +26,20 @@ import org.gradle.api.tasks.bundling.Zip /** * A [Plugin] which packages and runs a project as a Java Application using the shadowed jar. * - * Modified from [org.gradle.api.plugins.ApplicationPlugin.java](https://github.com/gradle/gradle/blob/fdecc3c95828bb9a1c1bb6114483fe5b16f9159d/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java). + * Modified from + * [org.gradle.api.plugins.ApplicationPlugin.java](https://github.com/gradle/gradle/blob/fdecc3c95828bb9a1c1bb6114483fe5b16f9159d/platforms/jvm/plugins-application/src/main/java/org/gradle/api/plugins/ApplicationPlugin.java). * * @see [ApplicationPlugin] */ public abstract class ShadowApplicationPlugin : Plugin { - override fun apply(project: Project): Unit = with(project) { - addRunTask() - addCreateScriptsTask() - configureDistribution() - configureShadowJarMainClass() - configureInstallTask() - } + override fun apply(project: Project): Unit = + with(project) { + addRunTask() + addCreateScriptsTask() + configureDistribution() + configureShadowJarMainClass() + configureInstallTask() + } protected open fun Project.addRunTask() { tasks.register(SHADOW_RUN_TASK_NAME, JavaExec::class.java) { task -> @@ -58,7 +60,8 @@ public abstract class ShadowApplicationPlugin : Plugin { protected open fun Project.addCreateScriptsTask() { tasks.register(SHADOW_SCRIPTS_TASK_NAME, CreateStartScripts::class.java) { task -> - task.description = "Creates OS specific scripts to run the project as a JVM application using the shadow jar" + task.description = + "Creates OS specific scripts to run the project as a JVM application using the shadow jar" task.classpath = files(tasks.shadowJar) @@ -67,7 +70,9 @@ public abstract class ShadowApplicationPlugin : Plugin { task.mainModule.convention(mainModule) task.mainClass.convention(mainClass) task.conventionMapping.map("applicationName", ::getApplicationName) - task.conventionMapping.map("outputDir") { layout.buildDirectory.dir("scriptsShadow").get().asFile } + task.conventionMapping.map("outputDir") { + layout.buildDirectory.dir("scriptsShadow").get().asFile + } task.conventionMapping.map("executableDir", ::getExecutableDir) task.conventionMapping.map("defaultJvmOpts", ::getApplicationDefaultJvmArgs) } @@ -82,17 +87,18 @@ public abstract class ShadowApplicationPlugin : Plugin { task.doFirst("Check installation directory") { val destinationDir = task.destinationDir - val children = destinationDir.list() ?: throw IOException("Could not list directory $destinationDir") + val children = + destinationDir.list() ?: throw IOException("Could not list directory $destinationDir") if (children.isEmpty()) return@doFirst if ( !destinationDir.resolve("lib").isDirectory || - !destinationDir.resolve("bin").isDirectory || - !destinationDir.resolve(executableDir.get()).isDirectory + !destinationDir.resolve("bin").isDirectory || + !destinationDir.resolve(executableDir.get()).isDirectory ) { throw GradleException( "The specified installation directory '$destinationDir' is neither empty nor does it contain an installation for '${applicationName.get()}'.\n" + "If you really want to install to this directory, delete it and run the install task again.\n" + - "Alternatively, choose a different installation directory.", + "Alternatively, choose a different installation directory." ) } } @@ -103,10 +109,12 @@ public abstract class ShadowApplicationPlugin : Plugin { distributions.register(DISTRIBUTION_NAME) { dist -> dist.distributionBaseName.convention( provider { - // distributionBaseName defaults to `$project.name-$distribution.name`, applicationName defaults to project.name - // so we append the suffix to match the default distributionBaseName. Modified from `ApplicationPlugin.configureDistribution()`. + // distributionBaseName defaults to `$project.name-$distribution.name`, applicationName + // defaults to project.name + // so we append the suffix to match the default distributionBaseName. Modified from + // `ApplicationPlugin.configureDistribution()`. "${applicationExtension.applicationName}-$DISTRIBUTION_NAME" - }, + } ) dist.contents { distSpec -> distSpec.from(file("src/dist")) @@ -126,15 +134,11 @@ public abstract class ShadowApplicationPlugin : Plugin { } protected open fun Project.configureShadowJarMainClass() { - tasks.shadowJar.configure { task -> - task.mainClass.convention(applicationExtension.mainClass) - } + tasks.shadowJar.configure { task -> task.mainClass.convention(applicationExtension.mainClass) } } public companion object { - /** - * Reflects the number of 755. - */ + /** Reflects the number of 755. */ private const val UNIX_SCRIPT_PERMISSIONS = "rwxr-xr-x" public const val DISTRIBUTION_NAME: String = SHADOW diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index c1c4f22a4..862c497c0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -14,20 +14,20 @@ import org.gradle.api.plugins.ExtensionContainer public abstract class ShadowBasePlugin : Plugin { - override fun apply(project: Project): Unit = with(project) { - with(extensions.create(EXTENSION_NAME, ShadowExtension::class.java)) { - addShadowVariantIntoJavaComponent.convention(true) - addTargetJvmVersionAttribute.convention(true) - bundlingAttribute.convention(Bundling.SHADOWED) + override fun apply(project: Project): Unit = + with(project) { + with(extensions.create(EXTENSION_NAME, ShadowExtension::class.java)) { + addShadowVariantIntoJavaComponent.convention(true) + addTargetJvmVersionAttribute.convention(true) + bundlingAttribute.convention(Bundling.SHADOWED) + } + @Suppress("EagerGradleConfiguration") // this should be created eagerly. + configurations.create(CONFIGURATION_NAME) } - @Suppress("EagerGradleConfiguration") // this should be created eagerly. - configurations.create(CONFIGURATION_NAME) - } public companion object { /** * Most of the components registered by Shadow plugin will use this name (`shadow`). - * * - [ExtensionContainer.create] * - [ConfigurationContainer.register] * - [SoftwareComponentContainer.register] diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index b74387e8a..8cf5927c8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -6,16 +6,18 @@ import org.gradle.api.provider.Property public interface ShadowExtension { /** - * If `true`, publishes the [ShadowJavaPlugin.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME] as an optional variant of - * the `java` component. This affects how consumers resolve the published artifact. + * If `true`, publishes the [ShadowJavaPlugin.SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME] as an + * optional variant of the `java` component. This affects how consumers resolve the published + * artifact. * * Defaults to `true`. */ public val addShadowVariantIntoJavaComponent: Property /** - * If `true`, adds a [TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE] attribute to the Gradle Module Metadata of the - * shadowed JAR. This affects how consumers resolve the published artifact based on the target JVM version. + * If `true`, adds a [TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE] attribute to the Gradle + * Module Metadata of the shadowed JAR. This affects how consumers resolve the published artifact + * based on the target JVM version. * * Defaults to `true`. */ @@ -24,7 +26,8 @@ public interface ShadowExtension { /** * The [Bundling] attribute to use for the Gradle Module Metadata. * - * Per description of the attribute, you should set it to either [Bundling.SHADOWED] or [Bundling.EMBEDDED]. + * Per description of the attribute, you should set it to either [Bundling.SHADOWED] or + * [Bundling.EMBEDDED]. * * Defaults to [Bundling.SHADOWED]. */ 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 f58b81e39..55c86d5da 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 @@ -26,22 +26,24 @@ import org.gradle.api.plugins.JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.tasks.bundling.Jar -public abstract class ShadowJavaPlugin @Inject constructor( - private val softwareComponentFactory: SoftwareComponentFactory, -) : Plugin { +public abstract class ShadowJavaPlugin +@Inject +constructor(private val softwareComponentFactory: SoftwareComponentFactory) : Plugin { - override fun apply(project: Project): Unit = with(project) { - configureShadowJar() - configureConfigurations() - configureComponents() - configureJavaGradlePlugin() - } + override fun apply(project: Project): Unit = + with(project) { + configureShadowJar() + configureConfigurations() + configureComponents() + configureJavaGradlePlugin() + } protected open fun Project.configureShadowJar() { - val taskProvider = registerShadowJarCommon(tasks.named("jar", Jar::class.java)) { task -> - task.from(sourceSets.named("main").map { it.output }) - task.configurations.convention(provider { listOf(runtimeConfiguration) }) - } + val taskProvider = + registerShadowJarCommon(tasks.named("jar", Jar::class.java)) { task -> + task.from(sourceSets.named("main").map { it.output }) + task.configurations.convention(provider { listOf(runtimeConfiguration) }) + } artifacts.add(configurations.shadow.name, taskProvider) } @@ -50,42 +52,61 @@ public abstract class ShadowJavaPlugin @Inject constructor( configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME) { compileClasspath -> compileClasspath.extendsFrom(shadowConfiguration) } - val shadowRuntimeElements = configurations.register(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { - it.extendsFrom(shadowConfiguration) - it.isCanBeConsumed = true - it.isCanBeResolved = false - it.attributes { attrs -> - attrs.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_RUNTIME)) - attrs.attribute(Category.CATEGORY_ATTRIBUTE, objects.named(Category::class.java, Category.LIBRARY)) - attrs.attribute( - LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, - objects.named(LibraryElements::class.java, LibraryElements.JAR), - ) - attrs.attributeProvider( - Bundling.BUNDLING_ATTRIBUTE, - shadow.bundlingAttribute.map { attr -> objects.named(Bundling::class.java, attr) }, - ) + val shadowRuntimeElements = + configurations.register(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) { + it.extendsFrom(shadowConfiguration) + it.isCanBeConsumed = true + it.isCanBeResolved = false + it.attributes { attrs -> + attrs.attribute( + Usage.USAGE_ATTRIBUTE, + objects.named(Usage::class.java, Usage.JAVA_RUNTIME), + ) + attrs.attribute( + Category.CATEGORY_ATTRIBUTE, + objects.named(Category::class.java, Category.LIBRARY), + ) + attrs.attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + objects.named(LibraryElements::class.java, LibraryElements.JAR), + ) + attrs.attributeProvider( + Bundling.BUNDLING_ATTRIBUTE, + shadow.bundlingAttribute.map { attr -> objects.named(Bundling::class.java, attr) }, + ) + } + it.outgoing.artifact(tasks.shadowJar) } - it.outgoing.artifact(tasks.shadowJar) - } // Must use afterEvaluate here as we need to track the changes of addTargetJvmVersionAttribute. afterEvaluate { if (shadow.addTargetJvmVersionAttribute.get()) { - logger.info("Setting ${TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name} attribute for ${shadowRuntimeElements.name} configuration.") + logger.info( + "Setting ${TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name} attribute for ${shadowRuntimeElements.name} configuration." + ) } else { - logger.info("Skipping setting ${TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name} attribute for ${shadowRuntimeElements.name} configuration.") + logger.info( + "Skipping setting ${TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE.name} attribute for ${shadowRuntimeElements.name} configuration." + ) return@afterEvaluate } - val targetJvmVersion = configurations.named(COMPILE_CLASSPATH_CONFIGURATION_NAME).map { compileClasspath -> - compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) - }.getOrElse(javaPluginExtension.targetCompatibility.majorVersion.toInt()) + val targetJvmVersion = + configurations + .named(COMPILE_CLASSPATH_CONFIGURATION_NAME) + .map { compileClasspath -> + compileClasspath.attributes.getAttribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE) + } + .getOrElse(javaPluginExtension.targetCompatibility.majorVersion.toInt()) // https://github.com/gradle/gradle/blob/4198ab0670df14af9f77b9098dc892b199ac1f3f/platforms/jvm/plugins-java-base/src/main/java/org/gradle/api/plugins/jvm/internal/DefaultJvmLanguageUtilities.java#L85-L87 if (targetJvmVersion == Int.MAX_VALUE) { - logger.info("Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases.") + logger.info( + "Cannot set the target JVM version to Int.MAX_VALUE when `java.autoTargetJvmDisabled` is enabled or in other cases." + ) } else { - logger.info("Setting target JVM version to $targetJvmVersion for ${shadowRuntimeElements.name} configuration.") + logger.info( + "Setting target JVM version to $targetJvmVersion for ${shadowRuntimeElements.name} configuration." + ) shadowRuntimeElements.configure { it.attributes { attrs -> attrs.attribute(TargetJvmVersion.TARGET_JVM_VERSION_ATTRIBUTE, targetJvmVersion) @@ -102,7 +123,8 @@ public abstract class ShadowJavaPlugin @Inject constructor( shadowComponent.addVariantsFromConfigurationCompat(shadowRuntimeElements) { variant -> variant.mapToMavenScope("runtime") } - // Must use afterEvaluate here as we need to track the changes of addShadowVariantIntoJavaComponent. + // Must use afterEvaluate here as we need to track the changes of + // addShadowVariantIntoJavaComponent. afterEvaluate { if (shadow.addShadowVariantIntoJavaComponent.get()) { logger.info("Adding ${shadowRuntimeElements.name} variant to Java component.") @@ -124,13 +146,16 @@ public abstract class ShadowJavaPlugin @Inject constructor( val gradleApi = dependencies.gradleApi() // Remove the gradleApi so it isn't merged into the jar file. // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. - // See https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161. + // See + // https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161. configurations.named(API_CONFIGURATION_NAME) { api -> // Only proceed if the removal is successful. if (!api.dependencies.remove(gradleApi)) return@named // Compile only gradleApi() to make sure the plugin can compile against Gradle API. - configurations.getByName(COMPILE_ONLY_CONFIGURATION_NAME) - .dependencies.add(dependencies.gradleApi()) + configurations + .getByName(COMPILE_ONLY_CONFIGURATION_NAME) + .dependencies + .add(dependencies.gradleApi()) } } } @@ -140,7 +165,8 @@ public abstract class ShadowJavaPlugin @Inject constructor( public const val SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME: String = "shadowRuntimeElements" @get:JvmSynthetic - public inline val ConfigurationContainer.shadowRuntimeElements: NamedDomainObjectProvider + public inline val ConfigurationContainer.shadowRuntimeElements: + NamedDomainObjectProvider get() = named(SHADOW_RUNTIME_ELEMENTS_CONFIGURATION_NAME) } } 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 3467cd562..441ad5b5d 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 @@ -12,19 +12,24 @@ import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget public abstract class ShadowKmpPlugin : Plugin { - override fun apply(project: Project): Unit = with(project) { - extensions.getByType(KotlinMultiplatformExtension::class.java).targets.configureEach { target -> - if (target !is KotlinJvmTarget) return@configureEach - @Suppress("EagerGradleConfiguration") - if (tasks.findByName(SHADOW_JAR_TASK_NAME) != null) { - // Declaring multiple Kotlin Targets of the same type is not supported. See https://kotl.in/declaring-multiple-targets for more details. - logger.info("$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: ${target.name}") - return@configureEach - } + override fun apply(project: Project): Unit = + with(project) { + extensions.getByType(KotlinMultiplatformExtension::class.java).targets.configureEach { target + -> + if (target !is KotlinJvmTarget) return@configureEach + @Suppress("EagerGradleConfiguration") + if (tasks.findByName(SHADOW_JAR_TASK_NAME) != null) { + // Declaring multiple Kotlin Targets of the same type is not supported. See + // https://kotl.in/declaring-multiple-targets for more details. + logger.info( + "$SHADOW_JAR_TASK_NAME task already exists, skipping configuration for target: ${target.name}" + ) + return@configureEach + } - configureShadowJar(target) + configureShadowJar(target) + } } - } private fun Project.configureShadowJar(target: KotlinJvmTarget) { val kotlinJvmMain = target.compilations.named("main") @@ -33,15 +38,13 @@ public abstract class ShadowKmpPlugin : Plugin { task.configurations.convention( provider { listOf(configurations.getByName(kotlinJvmMain.get().runtimeDependencyConfigurationName)) - }, + } ) if (!isAtLeastKgp("1.9.0")) return@registerShadowJarCommon @OptIn(ExperimentalKotlinGradlePluginApi::class) - target.mainRun { - task.mainClass.convention(mainClass) - } + target.mainRun { task.mainClass.convention(mainClass) } } } } 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 de22b3038..8b5d9d18b 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 @@ -9,37 +9,36 @@ import org.gradle.api.Project public abstract class ShadowPlugin : Plugin { - override fun apply(project: Project): Unit = with(project.plugins) { - apply(ShadowBasePlugin::class.java) - // org.gradle.api.plugins.JavaPlugin - withId("org.gradle.java") { - apply(ShadowJavaPlugin::class.java) - } - // org.gradle.api.plugins.ApplicationPlugin - withId("org.gradle.application") { - apply(ShadowApplicationPlugin::class.java) - } - withId(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { - apply(ShadowKmpPlugin::class.java) - } - withId("com.android.base") { - error( - "Shadow does not support using with AGP, you may need Android Fused Library plugin instead. " + - "See https://developer.android.com/build/publish-library/fused-library", - ) - } - project.configureBuildScan() + override fun apply(project: Project): Unit = + with(project.plugins) { + apply(ShadowBasePlugin::class.java) + // org.gradle.api.plugins.JavaPlugin + withId("org.gradle.java") { apply(ShadowJavaPlugin::class.java) } + // org.gradle.api.plugins.ApplicationPlugin + withId("org.gradle.application") { apply(ShadowApplicationPlugin::class.java) } + withId(KOTLIN_MULTIPLATFORM_PLUGIN_ID) { apply(ShadowKmpPlugin::class.java) } + withId("com.android.base") { + error( + "Shadow does not support using with AGP, you may need Android Fused Library plugin instead. " + + "See https://developer.android.com/build/publish-library/fused-library" + ) + } + project.configureBuildScan() - // 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. - // If the user applies shadow before those plugins. However, this is fine, because this was also - // the behavior with the old plugin when applying in that order. - apply(LegacyShadowPlugin::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. + // If the user applies shadow before those plugins. However, this is fine, because this was + // also + // the behavior with the old plugin when applying in that order. + apply(LegacyShadowPlugin::class.java) + } private fun Project.configureBuildScan() { - val enableDevelocityIntegration = findOptionalProperty(ENABLE_DEVELOCITY_INTEGRATION_PROPERTY)?.toBoolean() ?: false + val enableDevelocityIntegration = + findOptionalProperty(ENABLE_DEVELOCITY_INTEGRATION_PROPERTY)?.toBoolean() ?: false if (enableDevelocityIntegration) { logger.info("Enabling Develocity integration for Shadow plugin.") } else { @@ -50,6 +49,7 @@ public abstract class ShadowPlugin : Plugin { } public companion object { - public const val ENABLE_DEVELOCITY_INTEGRATION_PROPERTY: String = "com.gradleup.shadow.enableDevelocityIntegration" + public const val ENABLE_DEVELOCITY_INTEGRATION_PROPERTY: String = + "com.gradleup.shadow.enableDevelocityIntegration" } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt index 4033738dd..c743b14b8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultDependencyFilter.kt @@ -4,16 +4,16 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter import org.gradle.api.Project import org.gradle.api.artifacts.ResolvedDependency -internal class DefaultDependencyFilter( - project: Project, -) : DependencyFilter.AbstractDependencyFilter(project) { +internal class DefaultDependencyFilter(project: Project) : + DependencyFilter.AbstractDependencyFilter(project) { override fun resolve( dependencies: Set, includedDependencies: MutableSet, excludedDependencies: MutableSet, ) { dependencies.forEach { dep -> - val added = if (dep.isIncluded()) includedDependencies.add(dep) else excludedDependencies.add(dep) + val added = + if (dep.isIncluded()) includedDependencies.add(dep) else excludedDependencies.add(dep) if (added) { resolve(dep.children, includedDependencies, excludedDependencies) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt index 247cafbbf..887a26419 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/DefaultInheritManifest.kt @@ -16,17 +16,12 @@ internal class DefaultInheritManifest( manifest: Manifest? = null, // `AbstractTask.getServices` is protected, we need to get it via `DefaultProject`. // https://github.com/gradle/gradle/blob/master/subprojects/core/src/main/java/org/gradle/api/internal/AbstractTask.java#L194 - private val fileResolver: FileResolver = (project as DefaultProject).services.get(FileResolver::class.java), + private val fileResolver: FileResolver = + (project as DefaultProject).services.get(FileResolver::class.java), private val internalManifest: Manifest = manifest ?: DefaultManifest(fileResolver), -) : InheritManifest, - Manifest by internalManifest { +) : InheritManifest, Manifest by internalManifest { - override fun inheritFrom( - vararg inheritPaths: Any, - action: Action, - ) { - inheritPaths.forEach { - from(it, action) - } + override fun inheritFrom(vararg inheritPaths: Any, action: Action) { + inheritPaths.forEach { from(it, action) } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index 03c281209..ae7f649e3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -24,12 +24,11 @@ import org.gradle.api.tasks.SourceSetContainer import org.gradle.jvm.toolchain.JavaToolchainService import org.gradle.util.GradleVersion -/** - * Return `runtimeClasspath` or `runtime` configuration. - */ +/** Return `runtimeClasspath` or `runtime` configuration. */ internal inline val Project.runtimeConfiguration: Configuration - get() = configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) - ?: configurations.getByName("runtime") + get() = + configurations.findByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME) + ?: configurations.getByName("runtime") internal inline val Project.sourceSets: SourceSetContainer get() = extensions.getByType(SourceSetContainer::class.java) @@ -46,8 +45,10 @@ internal inline val Project.javaPluginExtension: JavaPluginExtension internal inline val Project.javaToolchainService: JavaToolchainService get() = extensions.getByType(JavaToolchainService::class.java) -// ExtraPropertiesExtension is IP safe and contains properties from both the root `gradle.properties` and the -// subproject's `gradle.properties`. See https://github.com/gradle/gradle/issues/29600#issuecomment-3580868326. +// ExtraPropertiesExtension is IP safe and contains properties from both the root +// `gradle.properties` and the +// subproject's `gradle.properties`. See +// https://github.com/gradle/gradle/issues/29600#issuecomment-3580868326. internal fun Project.findOptionalProperty(propertyName: String): String? { val extras = checkNotNull(extensions.findByType(ExtraPropertiesExtension::class.java)) return if (extras.has(propertyName)) extras.get(propertyName)?.toString() else null @@ -64,9 +65,7 @@ internal fun Project.addBuildScanCustomValues() { } } -/** - * TODO: this could be removed after bumping the min Gradle requirement to 9.2 or above. - */ +/** TODO: this could be removed after bumping the min Gradle requirement to 9.2 or above. */ @Suppress("UnstableApiUsage") internal fun AdhocComponentWithVariants.addVariantsFromConfigurationCompat( outgoingConfiguration: NamedDomainObjectProvider, @@ -81,44 +80,42 @@ internal fun AdhocComponentWithVariants.addVariantsFromConfigurationCompat( } internal inline fun ObjectFactory.property( - defaultValue: Any? = null, -): Property = property(V::class.java).apply { - defaultValue ?: return@apply - if (defaultValue is Provider<*>) { - @Suppress("UNCHECKED_CAST") - convention(defaultValue as Provider) - } else { - convention(defaultValue as V) + defaultValue: Any? = null +): Property = + property(V::class.java).apply { + defaultValue ?: return@apply + if (defaultValue is Provider<*>) { + @Suppress("UNCHECKED_CAST") convention(defaultValue as Provider) + } else { + convention(defaultValue as V) + } } -} @Suppress("UNCHECKED_CAST") internal inline fun ObjectFactory.setProperty( - defaultValue: Any? = null, -): SetProperty = setProperty(V::class.java).apply { - defaultValue ?: return@apply - if (defaultValue is Provider<*>) { - convention(defaultValue as Provider>) - } else { - convention(defaultValue as Iterable) + defaultValue: Any? = null +): SetProperty = + setProperty(V::class.java).apply { + defaultValue ?: return@apply + if (defaultValue is Provider<*>) { + convention(defaultValue as Provider>) + } else { + convention(defaultValue as Iterable) + } } -} @Suppress("UNCHECKED_CAST") internal inline fun ObjectFactory.mapProperty( - defaultValue: Any? = null, -): MapProperty = mapProperty(String::class.java, V::class.java).apply { - defaultValue ?: return@apply - if (defaultValue is Provider<*>) { - convention(defaultValue as Provider>) - } else { - convention(defaultValue as Map) + defaultValue: Any? = null +): MapProperty = + mapProperty(String::class.java, V::class.java).apply { + defaultValue ?: return@apply + if (defaultValue is Provider<*>) { + convention(defaultValue as Provider>) + } else { + convention(defaultValue as Map) + } } -} -internal inline fun ObjectFactory.fileCollection( - path: () -> Any, -): ConfigurableFileCollection = fileCollection().apply { - @Suppress("UnstableApiUsage") - convention(path()) -} +internal inline fun ObjectFactory.fileCollection(path: () -> Any): ConfigurableFileCollection = + fileCollection().apply { @Suppress("UnstableApiUsage") convention(path()) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt index 0ee204f86..75fdd5d46 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/MinimizeDependencyFilter.kt @@ -4,20 +4,20 @@ import com.github.jengelman.gradle.plugins.shadow.tasks.DependencyFilter import org.gradle.api.Project import org.gradle.api.artifacts.ResolvedDependency -internal class MinimizeDependencyFilter( - project: Project, -) : DependencyFilter.AbstractDependencyFilter(project) { +internal class MinimizeDependencyFilter(project: Project) : + DependencyFilter.AbstractDependencyFilter(project) { override fun resolve( dependencies: Set, includedDependencies: MutableSet, excludedDependencies: MutableSet, ) { dependencies.forEach { dep -> - val added = if (dep.isIncluded() && !excludedDependencies.any { it in dep.parents }) { - includedDependencies.add(dep) - } else { - excludedDependencies.add(dep) - } + val added = + if (dep.isIncluded() && !excludedDependencies.any { it in dep.parents }) { + includedDependencies.add(dep) + } else { + excludedDependencies.add(dep) + } if (added) { resolve(dep.children, includedDependencies, excludedDependencies) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index ccf2bd659..ac30aeb81 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -8,8 +8,10 @@ import org.objectweb.asm.Opcodes import org.objectweb.asm.commons.Remapper /** - * Modified from [org.apache.maven.plugins.shade.DefaultShader.RelocatorRemapper](https://github.com/apache/maven-shade-plugin/blob/83c123d1f9c5f6927af2aca12ee322b5168a7c63/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java#L689-L772). - * Modified from [org.apache.maven.plugins.shade.DefaultShader.DefaultPackageMapper](https://github.com/apache/maven-shade-plugin/blob/199ffaecd26a912527173ed4edae366e48a00998/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java#L737-L774). + * Modified from + * [org.apache.maven.plugins.shade.DefaultShader.RelocatorRemapper](https://github.com/apache/maven-shade-plugin/blob/83c123d1f9c5f6927af2aca12ee322b5168a7c63/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java#L689-L772). + * Modified from + * [org.apache.maven.plugins.shade.DefaultShader.DefaultPackageMapper](https://github.com/apache/maven-shade-plugin/blob/199ffaecd26a912527173ed4edae366e48a00998/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java#L737-L774). * * @author John Engelman */ @@ -30,9 +32,7 @@ internal class RelocatorRemapper( private fun mapName(name: String, mapLiterals: Boolean = false): String { // Maybe a list of types. - val newName = name.split(';').joinToString(";") { - mapNameImpl(it, mapLiterals) - } + val newName = name.split(';').joinToString(";") { mapNameImpl(it, mapLiterals) } if (newName != name) { onModified() @@ -66,9 +66,7 @@ internal class RelocatorRemapper( } private companion object { - /** - * https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html - */ + /** https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html */ val classPattern: Pattern = Pattern.compile("([\\[()BCDFIJSZ]*)?L([^;]+);?") } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproducibleProperties.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproducibleProperties.kt index 02c3a3097..044becfb8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproducibleProperties.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproducibleProperties.kt @@ -6,9 +6,7 @@ import java.io.Writer import java.nio.charset.Charset import java.util.Properties -/** - * Provides functionality for reproducible serialization. - */ +/** Provides functionality for reproducible serialization. */ internal class ReproducibleProperties : Properties() { override fun store(writer: Writer, comments: String) { throw UnsupportedOperationException("use writeWithoutComments()") @@ -19,19 +17,21 @@ internal class ReproducibleProperties : Properties() { } fun writeWithoutComments(charset: Charset, os: OutputStream) { - val bufferedReader = StringWriter().apply { - super.store(this, null) - }.toString().reader().buffered() + val bufferedReader = + StringWriter().apply { super.store(this, null) }.toString().reader().buffered() - os.bufferedWriter(charset).apply { - var line: String? = null - while (bufferedReader.readLine().also { line = it } != null && line != null) { - if (!line.startsWith("#")) { - write(line) - newLine() + os + .bufferedWriter(charset) + .apply { + var line: String? = null + while (bufferedReader.readLine().also { line = it } != null && line != null) { + if (!line.startsWith("#")) { + write(line) + newLine() + } } } - }.flush() + .flush() } override val entries: MutableSet> diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index 1c6f9d97a..e9b09ba44 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -22,7 +22,9 @@ internal class UnusedTracker( private val cp = Clazzpath() init { - projectUnits = sourceSetsClassesDirs.map { cp.addClazzpathUnit(it) } + classJars.map { cp.addClazzpathUnit(it) } + projectUnits = + sourceSetsClassesDirs.map { cp.addClazzpathUnit(it) } + + classJars.map { cp.addClazzpathUnit(it) } } fun findUnused(): Set { @@ -42,8 +44,8 @@ internal class UnusedTracker( companion object { fun getApiJarsFromProject(project: Project): FileCollection { - val apiDependencies = project.configurations.findByName("api")?.dependencies - ?: return project.files() + val apiDependencies = + project.configurations.findByName("api")?.dependencies ?: return project.files() val runtimeConfiguration = project.runtimeConfiguration val apiJars = mutableListOf() apiDependencies.forEach { dep -> @@ -59,7 +61,8 @@ internal class UnusedTracker( is ExternalModuleDependency -> Unit else -> { addJar(runtimeConfiguration, dep, apiJars) - val jarFile = runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } ?: return@forEach + val jarFile = + runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } ?: return@forEach apiJars.add(jarFile) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt index 2845bc1ea..a28e06d5b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/Utils.kt @@ -8,19 +8,13 @@ import java.util.Properties import java.util.jar.Attributes.Name as JarAttributeName import org.apache.tools.zip.ZipEntry -/** - * Known as `Main-Class` in the manifest file. - */ +/** Known as `Main-Class` in the manifest file. */ internal val mainClassAttributeKey = JarAttributeName.MAIN_CLASS.toString() -/** - * Known as `Class-Path` in the manifest file. - */ +/** Known as `Class-Path` in the manifest file. */ internal val classPathAttributeKey = JarAttributeName.CLASS_PATH.toString() -/** - * Known as `Multi-Release` in the manifest file. - */ +/** Known as `Multi-Release` in the manifest file. */ internal val multiReleaseAttributeKey = JarAttributeName.MULTI_RELEASE.toString() /** @@ -30,31 +24,31 @@ internal val multiReleaseAttributeKey = JarAttributeName.MULTI_RELEASE.toString( internal inline fun Any?.cast(): T = this as T @Suppress("NOTHING_TO_INLINE") -internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy = lazy(LazyThreadSafetyMode.NONE, initializer) +internal inline fun unsafeLazy(noinline initializer: () -> T): Lazy = + lazy(LazyThreadSafetyMode.NONE, initializer) internal inline fun zipEntry( name: String, preserveLastModified: Boolean = true, lastModified: Long = -1, block: ZipEntry.() -> Unit = {}, -): ZipEntry = ZipEntry(name).apply { - if (preserveLastModified) { - if (lastModified >= 0) { - time = lastModified +): ZipEntry = + ZipEntry(name).apply { + if (preserveLastModified) { + if (lastModified >= 0) { + time = lastModified + } + } else { + time = ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES } - } else { - time = ShadowCopyAction.CONSTANT_TIME_FOR_ZIP_ENTRIES + block() } - block() -} internal fun Properties.inputStream( charset: Charset = Charsets.ISO_8859_1, comments: String = "", ): ByteArrayInputStream { val os = ByteArrayOutputStream() - os.writer(charset).use { writer -> - store(writer, comments) - } + os.writer(charset).use { writer -> store(writer, comments) } return os.toByteArray().inputStream() } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt index 00fcc90ac..f7ada4233 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocationContext.kt @@ -1,12 +1,8 @@ package com.github.jengelman.gradle.plugins.shadow.relocation -public data class RelocateClassContext( - public val className: String, -) +public data class RelocateClassContext(public val className: String) -public data class RelocatePathContext( - public val path: String, -) +public data class RelocatePathContext(public val path: String) public fun Relocator.relocateClass(className: String): String { return relocateClass(RelocateClassContext(className)) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt index 424ee4508..774b0bed9 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/Relocator.kt @@ -4,7 +4,8 @@ import com.github.jengelman.gradle.plugins.shadow.transformers.CacheableTransfor import org.gradle.api.tasks.Input /** - * Modified from [org.apache.maven.plugins.shade.relocation.Relocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java). + * Modified from + * [org.apache.maven.plugins.shade.relocation.Relocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java). * * @author Jason van Zyl * @author John Engelman @@ -26,13 +27,14 @@ public interface Relocator { * Defaults to `false`. */ @get:Input - public val skipStringConstants: Boolean get() = false + public val skipStringConstants: Boolean + get() = false } /** - * Marks that a given instance of [Relocator] is compatible with the Gradle build cache. - * In other words, it has its appropriate inputs annotated so that Gradle can consider them when - * determining the cache key. + * Marks that a given instance of [Relocator] is compatible with the Gradle build cache. In other + * words, it has its appropriate inputs annotated so that Gradle can consider them when determining + * the cache key. * * @see [CacheableTransformer] */ diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index b4519f995..26676f766 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -7,14 +7,17 @@ import org.codehaus.plexus.util.SelectorUtils import org.gradle.api.tasks.Input /** - * Modified from [org.apache.maven.plugins.shade.relocation.SimpleRelocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java). + * Modified from + * [org.apache.maven.plugins.shade.relocation.SimpleRelocator.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java). * * @author Jason van Zyl * @author Mauro Talevi * @author John Engelman */ @CacheableRelocator -public open class SimpleRelocator @JvmOverloads constructor( +public open class SimpleRelocator +@JvmOverloads +constructor( pattern: String? = null, shadedPattern: String? = null, includes: List? = null, @@ -29,11 +32,9 @@ public open class SimpleRelocator @JvmOverloads constructor( private val sourcePackageExcludes = mutableSetOf() private val sourcePathExcludes = mutableSetOf() - @get:Input - public val includes: MutableSet = mutableSetOf() + @get:Input public val includes: MutableSet = mutableSetOf() - @get:Input - public val excludes: MutableSet = mutableSetOf() + @get:Input public val excludes: MutableSet = mutableSetOf() init { if (rawString) { @@ -74,13 +75,13 @@ public open class SimpleRelocator @JvmOverloads constructor( // Excludes should be subpackages of the global pattern. if (exclude.startsWith(this.pattern)) { sourcePackageExcludes.add( - exclude.substring(this.pattern.length).replaceFirst("[.][*]$".toRegex(), ""), + exclude.substring(this.pattern.length).replaceFirst("[.][*]$".toRegex(), "") ) } // Excludes should be subpackages of the global pattern. if (exclude.startsWith(pathPattern)) { sourcePathExcludes.add( - exclude.substring(pathPattern.length).replaceFirst("/[*]$".toRegex(), ""), + exclude.substring(pathPattern.length).replaceFirst("/[*]$".toRegex(), "") ) } } @@ -105,7 +106,9 @@ public open class SimpleRelocator @JvmOverloads constructor( // Allow for annoying option of an extra / on the front of a path. See MSHADE-119; // comes from getClass().getResource("/a/b/c.properties"). adjustedPath = adjustedPath.removePrefix("/") - return isIncluded(adjustedPath) && !isExcluded(adjustedPath) && adjustedPath.startsWith(pathPattern) + return isIncluded(adjustedPath) && + !isExcluded(adjustedPath) && + adjustedPath.startsWith(pathPattern) } override fun canRelocateClass(className: String): Boolean { @@ -127,11 +130,13 @@ public open class SimpleRelocator @JvmOverloads constructor( } /** - * We don't call this function now, so we don't have to expose [sourcePackageExcludes] and [sourcePathExcludes] as inputs. + * We don't call this function now, so we don't have to expose [sourcePackageExcludes] and + * [sourcePathExcludes] as inputs. */ override fun applyToSourceContent(sourceContent: String): String { if (rawString) return sourceContent - val content = shadeSourceWithExcludes(sourceContent, pattern, shadedPattern, sourcePackageExcludes) + val content = + shadeSourceWithExcludes(sourceContent, pattern, shadedPattern, sourcePackageExcludes) return shadeSourceWithExcludes(content, pathPattern, shadedPathPattern, sourcePathExcludes) } @@ -150,18 +155,19 @@ public open class SimpleRelocator @JvmOverloads constructor( excludes == other.excludes } - override fun hashCode(): Int = Objects.hash( - rawString, - skipStringConstants, - pattern, - pathPattern, - shadedPattern, - shadedPathPattern, - sourcePackageExcludes, - sourcePathExcludes, - includes, - excludes, - ) + override fun hashCode(): Int = + Objects.hash( + rawString, + skipStringConstants, + pattern, + pathPattern, + shadedPattern, + shadedPathPattern, + sourcePackageExcludes, + sourcePathExcludes, + includes, + excludes, + ) override fun toString(): String = buildString { append("SimpleRelocator(") @@ -187,27 +193,26 @@ public open class SimpleRelocator @JvmOverloads constructor( } private companion object { - /** - * Match dot, slash or space at end of string - */ + /** Match dot, slash or space at end of string */ val RX_ENDS_WITH_DOT_SLASH_SPACE: Pattern = Pattern.compile("[./ ]$") /** * Match - * - certain Java keywords + space - * - beginning of Javadoc link + optional line breaks and continuations with '*' - * - (opening curly brace / opening parenthesis / comma / equals / semicolon) + space - * - (closing curly brace / closing multi-line comment) + space + * - certain Java keywords + space + * - beginning of Javadoc link + optional line breaks and continuations with '*' + * - (opening curly brace / opening parenthesis / comma / equals / semicolon) + space + * - (closing curly brace / closing multi-line comment) + space * * at end of string */ - val RX_ENDS_WITH_JAVA_KEYWORD: Pattern = Pattern.compile( - "\\b(import|package|public|protected|private|static|final|synchronized|abstract|volatile|extends|implements|throws) $" + - "|" + - "\\{@link( \\*)* $" + - "|" + - "([{}(=;,]|\\*/) $", - ) + val RX_ENDS_WITH_JAVA_KEYWORD: Pattern = + Pattern.compile( + "\\b(import|package|public|protected|private|static|final|synchronized|abstract|volatile|extends|implements|throws) $" + + "|" + + "\\{@link( \\*)* $" + + "|" + + "([{}(=;,]|\\*/) $" + ) fun normalizePatterns(patterns: Collection?) = buildSet { patterns ?: return@buildSet @@ -220,14 +225,17 @@ public open class SimpleRelocator @JvmOverloads constructor( val fileName = FilenameUtils.getName(pattern) val fileParent = FilenameUtils.getPathNoEndSeparator(pattern) - val filePattern = if (!fileParent.isNullOrEmpty() && fileName.isNotEmpty()) { - // It's a file pattern like `kotlin/kotlin.kotlin_builtins`, so we don't need to normalize it. - pattern - } else { - pattern.replace('.', '/') - } + val filePattern = + if (!fileParent.isNullOrEmpty() && fileName.isNotEmpty()) { + // It's a file pattern like `kotlin/kotlin.kotlin_builtins`, so we don't need to + // normalize it. + pattern + } else { + pattern.replace('.', '/') + } add(filePattern) - // Actually, class patterns should just use 'foo.bar.*' ending with a single asterisk, but some users + // Actually, class patterns should just use 'foo.bar.*' ending with a single asterisk, but + // some users // mistake them for path patterns like 'my/path/**', so let us be a bit more lenient here. if (filePattern.endsWith("/*") || filePattern.endsWith("/**")) { val packagePattern = filePattern.take(filePattern.lastIndexOf('/')) @@ -242,11 +250,15 @@ public open class SimpleRelocator @JvmOverloads constructor( patternTo: String, excludedPatterns: Set, ): String { - // Usually shading makes package names a bit longer, so make buffer 10% bigger than original source. + // Usually shading makes package names a bit longer, so make buffer 10% bigger than original + // source. val shadedSourceContent = StringBuilder(sourceContent.length * 11 / 10) - // Make sure that search pattern starts at word boundary and that we look for literal ".", not regex jokers. - val snippets = sourceContent.split(("\\b" + patternFrom.replace(".", "[.]") + "\\b").toRegex()) - .filter(CharSequence::isNotEmpty) + // Make sure that search pattern starts at word boundary and that we look for literal ".", not + // regex jokers. + val snippets = + sourceContent + .split(("\\b" + patternFrom.replace(".", "[.]") + "\\b").toRegex()) + .filter(CharSequence::isNotEmpty) snippets.forEachIndexed { i, snippet -> val isFirstSnippet = i == 0 val previousSnippet = if (isFirstSnippet) "" else snippets[i - 1] @@ -261,12 +273,11 @@ public open class SimpleRelocator @JvmOverloads constructor( shadedSourceContent.append(snippet) } else { val previousSnippetOneLine = previousSnippet.replace("\\s+".toRegex(), " ") - val afterDotSlashSpace = RX_ENDS_WITH_DOT_SLASH_SPACE.matcher(previousSnippetOneLine).find() + val afterDotSlashSpace = + RX_ENDS_WITH_DOT_SLASH_SPACE.matcher(previousSnippetOneLine).find() val afterJavaKeyWord = RX_ENDS_WITH_JAVA_KEYWORD.matcher(previousSnippetOneLine).find() val shouldExclude = doExclude || afterDotSlashSpace && !afterJavaKeyWord - shadedSourceContent - .append(if (shouldExclude) patternFrom else patternTo) - .append(snippet) + shadedSourceContent.append(if (shouldExclude) patternFrom else patternTo).append(snippet) } } return shadedSourceContent.toString() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index 8a0d7a683..90d38555b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -13,34 +13,25 @@ import org.gradle.api.specs.Spec // DependencyFilter is used as Gradle Input in ShadowJar, so it must be Serializable. public interface DependencyFilter : Serializable { - /** - * Resolve a [configuration] against the [include]/[exclude] rules in the filter. - */ + /** Resolve a [configuration] against the [include]/[exclude] rules in the filter. */ public fun resolve(configuration: Configuration): FileCollection /** - * Resolve all [configurations] against the [include]/[exclude] rules in the filter and combine the results. + * Resolve all [configurations] against the [include]/[exclude] rules in the filter and combine + * the results. */ public fun resolve(configurations: Collection): FileCollection - /** - * Exclude dependencies that match the provided [spec]. - */ + /** Exclude dependencies that match the provided [spec]. */ public fun exclude(spec: Spec) - /** - * Include dependencies that match the provided [spec]. - */ + /** Include dependencies that match the provided [spec]. */ public fun include(spec: Spec) - /** - * Create a [Spec] that matches the provided project [notation]. - */ + /** Create a [Spec] that matches the provided project [notation]. */ public fun project(notation: Any): Spec - /** - * Create a [Spec] that matches the provided [dependencyNotation]. - */ + /** Create a [Spec] that matches the provided [dependencyNotation]. */ public fun dependency(dependencyNotation: Any): Spec public abstract class AbstractDependencyFilter( @@ -68,9 +59,9 @@ public interface DependencyFilter : Serializable { } override fun resolve(configurations: Collection): FileCollection { - return configurations.map { resolve(it) } - .reduceOrNull { acc, fileCollection -> acc + fileCollection } - ?: project.files() + return configurations + .map { resolve(it) } + .reduceOrNull { acc, fileCollection -> acc + fileCollection } ?: project.files() } override fun exclude(spec: Spec) { @@ -83,21 +74,24 @@ public interface DependencyFilter : Serializable { override fun project(notation: Any): Spec { @Suppress("UNCHECKED_CAST") - val realNotation = when (notation) { - is ProjectDependency -> return notation.toSpec() - is Provider<*> -> mapOf("path" to notation.get()) - is String -> mapOf("path" to notation) - is Map<*, *> -> notation as Map - else -> throw IllegalArgumentException("Unsupported notation type: ${notation::class.java}") - } + val realNotation = + when (notation) { + is ProjectDependency -> return notation.toSpec() + is Provider<*> -> mapOf("path" to notation.get()) + is String -> mapOf("path" to notation) + is Map<*, *> -> notation as Map + else -> + throw IllegalArgumentException("Unsupported notation type: ${notation::class.java}") + } return project.dependencies.project(realNotation).toSpec() } override fun dependency(dependencyNotation: Any): Spec { - val realNotation = when (dependencyNotation) { - is Provider<*> -> dependencyNotation.get() - else -> dependencyNotation - } + val realNotation = + when (dependencyNotation) { + is Provider<*> -> dependencyNotation.get() + else -> dependencyNotation + } return project.dependencies.create(realNotation).toSpec() } @@ -109,17 +103,24 @@ public interface DependencyFilter : Serializable { private fun Dependency.toSpec(): Spec { return Spec { resolvedDependency -> - val groupMatch = group?.let { - it == resolvedDependency.moduleGroup || resolvedDependency.moduleGroup.matches(it.toRegex()) - } ?: true - val nameMatch = name.let { - it == resolvedDependency.moduleName || resolvedDependency.moduleName.matches(it.toRegex()) - } - val versionMatch = version?.let { - // Version like `1.0.0+1` can't be converted to regex directly because `+` is a special character in regex. - // So we check for exact match first, then fallback to regex match. - it == resolvedDependency.moduleVersion || resolvedDependency.moduleVersion.matches(it.toRegex()) - } ?: true + val groupMatch = + group?.let { + it == resolvedDependency.moduleGroup || + resolvedDependency.moduleGroup.matches(it.toRegex()) + } ?: true + val nameMatch = + name.let { + it == resolvedDependency.moduleName || + resolvedDependency.moduleName.matches(it.toRegex()) + } + val versionMatch = + version?.let { + // Version like `1.0.0+1` can't be converted to regex directly because `+` is a special + // character in regex. + // So we check for exact match first, then fallback to regex match. + it == resolvedDependency.moduleVersion || + resolvedDependency.moduleVersion.matches(it.toRegex()) + } ?: true groupMatch && nameMatch && versionMatch } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath.kt index 3fc2fe0a1..ad34c7ac8 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/FindResourceInClasspath.kt @@ -14,11 +14,11 @@ import org.gradle.work.DisableCachingByDefault /** * Helper task to temporarily add to your build script to find resources in the classpath that were - * identified as duplicates by [com.github.jengelman.gradle.plugins.shadow.transformers.MergePropertiesResourceTransformer] or + * identified as duplicates by + * [com.github.jengelman.gradle.plugins.shadow.transformers.MergePropertiesResourceTransformer] or * [com.github.jengelman.gradle.plugins.shadow.transformers.DeduplicatingResourceTransformer]. * * First, add the task to your build script: - * * ```kotlin * val findResources by tasks.registering(FindResourceInClasspath::class) { * // add configurations to search for resources in dependency jars @@ -40,14 +40,11 @@ import org.gradle.work.DisableCachingByDefault */ @DisableCachingByDefault(because = "Not worth caching") public abstract class FindResourceInClasspath(private val patternSet: PatternSet) : - DefaultTask(), - PatternFilterable by patternSet { + DefaultTask(), PatternFilterable by patternSet { @Inject public constructor() : this(PatternSet()) - @get:InputFiles - @get:Classpath - public abstract val classpath: ConfigurableFileCollection + @get:InputFiles @get:Classpath public abstract val classpath: ConfigurableFileCollection @Input // Trigger task executions after includes changed. override fun getIncludes(): MutableSet = patternSet.includes @@ -55,8 +52,7 @@ public abstract class FindResourceInClasspath(private val patternSet: PatternSet @Input // Trigger task executions after excludes changed. override fun getExcludes(): MutableSet = patternSet.excludes - @get:Inject - protected abstract val archiveOperations: ArchiveOperations + @get:Inject protected abstract val archiveOperations: ArchiveOperations @TaskAction public fun findResources() { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 428c77fd2..f76439721 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -1,4 +1,6 @@ -@file:Suppress("InternalGradleApiUsage") // We have to use internal Gradle APIs to implement a CopyAction. +@file:Suppress( + "InternalGradleApiUsage" +) // We have to use internal Gradle APIs to implement a CopyAction. package com.github.jengelman.gradle.plugins.shadow.tasks @@ -35,7 +37,8 @@ import org.objectweb.asm.ClassWriter import org.objectweb.asm.commons.ClassRemapper /** - * Modified from [org.gradle.api.internal.file.archive.ZipCopyAction.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java). + * Modified from + * [org.gradle.api.internal.file.archive.ZipCopyAction.java](https://github.com/gradle/gradle/blob/b893c2b085046677cf858fb3d5ce00e68e556c3a/platforms/core-configuration/file-operations/src/main/java/org/gradle/api/internal/file/archive/ZipCopyAction.java). */ public open class ShadowCopyAction @Deprecated("This should not be used as a public API. Will be made internal in a future release.") @@ -53,17 +56,21 @@ constructor( private val visitedDirs = mutableMapOf() override fun execute(stream: CopyActionProcessingStream): WorkResult { - val zipOutStream = try { - zosProvider(zipFile) - } catch (e: Exception) { - throw GradleException("Could not create ZIP '$zipFile'.", e) - } + val zipOutStream = + try { + zosProvider(zipFile) + } catch (e: Exception) { + throw GradleException("Could not create ZIP '$zipFile'.", e) + } try { zipOutStream.use { zos -> stream.process(StreamAction(zos)) processTransformers(zos) - addDirs(zos) // This must be called after adding all file entries to avoid duplicate directories being added. + addDirs( + zos + ) // This must be called after adding all file entries to avoid duplicate directories being + // added. checkDuplicateEntries(zos) } } catch (e: Exception) { @@ -80,7 +87,8 @@ constructor( } ``` See: https://docs.gradle.org/current/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle.api.tasks.bundling.Zip:zip64 for more details. - """.trimIndent(), + """ + .trimIndent() ) } zipFile.delete() @@ -98,8 +106,7 @@ constructor( } private fun addDirs(zos: ZipOutputStream) { - @Suppress("UNCHECKED_CAST") - val entries = zos.entries.map { it.name } + @Suppress("UNCHECKED_CAST") val entries = zos.entries.map { it.name } val added = entries.toMutableSet() val currentTimeMillis = System.currentTimeMillis() @@ -108,32 +115,31 @@ constructor( val entryName = "$parent/" if (parent.isNotEmpty() && added.add(entryName)) { val details = visitedDirs[parent] - val (lastModified, flag) = if (details == null) { - currentTimeMillis to UnixStat.DEFAULT_DIR_PERM - } else { - details.lastModified to details.permissions.toUnixNumeric() - } - val entry = zipEntry(entryName, preserveFileTimestamps, lastModified) { - unixMode = UnixStat.DIR_FLAG or flag - } + val (lastModified, flag) = + if (details == null) { + currentTimeMillis to UnixStat.DEFAULT_DIR_PERM + } else { + details.lastModified to details.permissions.toUnixNumeric() + } + val entry = + zipEntry(entryName, preserveFileTimestamps, lastModified) { + unixMode = UnixStat.DIR_FLAG or flag + } zos.putNextEntry(entry) zos.closeEntry() addParent(parent) } } - entries.forEach { - addParent(it) - } + entries.forEach { addParent(it) } } private fun checkDuplicateEntries(zos: ZipOutputStream) { val entries = zos.entries.map { it.name } val duplicates = entries.groupingBy { it }.eachCount().filter { it.value > 1 } if (duplicates.isNotEmpty()) { - val dupEntries = duplicates.entries.joinToString(separator = "\n") { - "${it.key} (${it.value} times)" - } + val dupEntries = + duplicates.entries.joinToString(separator = "\n") { "${it.key} (${it.value} times)" } val message = "Duplicate entries found in the shadowed JAR: \n$dupEntries" if (failOnDuplicateEntries) { throw GradleException(message) @@ -143,9 +149,8 @@ constructor( } } - private inner class StreamAction( - private val zipOutStr: ZipOutputStream, - ) : CopyActionProcessingStreamAction { + private inner class StreamAction(private val zipOutStr: ZipOutputStream) : + CopyActionProcessingStreamAction { init { logger.info("Relocator count: ${relocators.size}.") if (encoding != null) { @@ -201,110 +206,122 @@ constructor( } /** - * Applies remapping to the given class with the specified relocation path. The remapped class is then written - * to the zip file. + * Applies remapping to the given class with the specified relocation path. The remapped class + * is then written to the zip file. */ - private fun FileCopyDetails.remapClass() = file.readBytes().let { bytes -> - var modified = false - val remapper = RelocatorRemapper(relocators) { modified = true } + private fun FileCopyDetails.remapClass() = + file.readBytes().let { bytes -> + var modified = false + val remapper = RelocatorRemapper(relocators) { modified = true } - // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant pool. - // Copying the original constant pool should be avoided because it would keep references - // to the original class names. This is not a problem at runtime (because these entries in the - // constant pool are never used), but confuses some tools such as Felix's maven-bundle-plugin - // that use the constant pool to determine the dependencies of a class. - val cw = ClassWriter(0) - val cr = ClassReader(bytes) - val cv = ClassRemapper(cw, remapper) + // We don't pass the ClassReader here. This forces the ClassWriter to rebuild the constant + // pool. + // Copying the original constant pool should be avoided because it would keep references + // to the original class names. This is not a problem at runtime (because these entries in + // the + // constant pool are never used), but confuses some tools such as Felix's + // maven-bundle-plugin + // that use the constant pool to determine the dependencies of a class. + val cw = ClassWriter(0) + val cr = ClassReader(bytes) + val cv = ClassRemapper(cw, remapper) - try { - cr.accept(cv, ClassReader.EXPAND_FRAMES) - } catch (t: Throwable) { - throw GradleException("Error in ASM processing class $path", t) - } + try { + cr.accept(cv, ClassReader.EXPAND_FRAMES) + } catch (t: Throwable) { + throw GradleException("Error in ASM processing class $path", t) + } - val newBytes = if (modified) { - cw.toByteArray() - } else { - // If we didn't need to change anything, keep the original bytes as-is - bytes - } + val newBytes = + if (modified) { + cw.toByteArray() + } else { + // If we didn't need to change anything, keep the original bytes as-is + bytes + } - // Temporarily remove the multi-release prefix. - val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() - val newPath = path.replace(multiReleasePrefix, "") - val relocatedPath = multiReleasePrefix + relocators.relocatePath(newPath) - try { - val entry = zipEntry(relocatedPath, preserveFileTimestamps, lastModified) { - unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() + // Temporarily remove the multi-release prefix. + val multiReleasePrefix = "^META-INF/versions/\\d+/".toRegex().find(path)?.value.orEmpty() + val newPath = path.replace(multiReleasePrefix, "") + val relocatedPath = multiReleasePrefix + relocators.relocatePath(newPath) + try { + val entry = + zipEntry(relocatedPath, preserveFileTimestamps, lastModified) { + unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() + } + // Now we put it back on so the class file is written out with the right extension. + zipOutStr.putNextEntry(entry) + zipOutStr.write(newBytes) + zipOutStr.closeEntry() + } catch (_: ZipException) { + logger.warn("We have a duplicate $relocatedPath in source project") } - // Now we put it back on so the class file is written out with the right extension. - zipOutStr.putNextEntry(entry) - zipOutStr.write(newBytes) - zipOutStr.closeEntry() - } catch (_: ZipException) { - logger.warn("We have a duplicate $relocatedPath in source project") } - } /** - * Applies remapping to the given kotlin module with the specified relocation path. - * The remapped module is then written to the zip file. + * Applies remapping to the given kotlin module with the specified relocation path. The remapped + * module is then written to the zip file. */ @OptIn(UnstableMetadataApi::class) - private fun FileCopyDetails.remapKotlinModule() = file.readBytes().let { bytes -> - val kmMetadata = KotlinModuleMetadata.read(bytes) - val newKmModule = KmModule().apply { - // We don't need to relocate the nested properties in `optionalAnnotationClasses`, there is a very special use case for Kotlin Multiplatform. - optionalAnnotationClasses += kmMetadata.kmModule.optionalAnnotationClasses - packageParts += kmMetadata.kmModule.packageParts.map { (pkg, parts) -> - val relocatedPkg = relocators.relocateClass(pkg) - val relocatedParts = KmPackageParts( - parts.fileFacades.mapTo(mutableListOf()) { relocators.relocatePath(it) }, - parts.multiFileClassParts.entries.associateTo(mutableMapOf()) { (name, facade) -> - relocators.relocatePath(name) to relocators.relocatePath(facade) - }, - ) - relocatedPkg to relocatedParts - } - } - val newKmMetadata = KotlinModuleMetadata(newKmModule, kmMetadata.version) + private fun FileCopyDetails.remapKotlinModule() = + file.readBytes().let { bytes -> + val kmMetadata = KotlinModuleMetadata.read(bytes) + val newKmModule = + KmModule().apply { + // We don't need to relocate the nested properties in `optionalAnnotationClasses`, there + // is a very special use case for Kotlin Multiplatform. + optionalAnnotationClasses += kmMetadata.kmModule.optionalAnnotationClasses + packageParts += + kmMetadata.kmModule.packageParts.map { (pkg, parts) -> + val relocatedPkg = relocators.relocateClass(pkg) + val relocatedParts = + KmPackageParts( + parts.fileFacades.mapTo(mutableListOf()) { relocators.relocatePath(it) }, + parts.multiFileClassParts.entries.associateTo(mutableMapOf()) { (name, facade) + -> + relocators.relocatePath(name) to relocators.relocatePath(facade) + }, + ) + relocatedPkg to relocatedParts + } + } + val newKmMetadata = KotlinModuleMetadata(newKmModule, kmMetadata.version) - val newBytes = newKmMetadata.write() - val relocatedPath = relocators.relocatePath(path) - val entryName = when { - relocatedPath != path -> relocatedPath - // Nothing changed, so keep the original path. - newBytes.contentEquals(bytes) -> path - // Content changed but path didn't, so rename to avoid name clash. The filename does not matter to the compiler. - else -> path.replace(".kotlin_module", ".shadow.kotlin_module") - } - val entry = zipEntry(entryName, preserveFileTimestamps, lastModified) { - unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() + val newBytes = newKmMetadata.write() + val relocatedPath = relocators.relocatePath(path) + val entryName = + when { + relocatedPath != path -> relocatedPath + // Nothing changed, so keep the original path. + newBytes.contentEquals(bytes) -> path + // Content changed but path didn't, so rename to avoid name clash. The filename does not + // matter to the compiler. + else -> path.replace(".kotlin_module", ".shadow.kotlin_module") + } + val entry = + zipEntry(entryName, preserveFileTimestamps, lastModified) { + unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() + } + zipOutStr.putNextEntry(entry) + zipOutStr.write(newBytes) + zipOutStr.closeEntry() } - zipOutStr.putNextEntry(entry) - zipOutStr.write(newBytes) - zipOutStr.closeEntry() - } private fun transform(fileDetails: FileCopyDetails, path: String): Boolean { val transformer = transformers.find { it.canTransformResource(fileDetails) } ?: return false fileDetails.file.inputStream().use { inputStream -> transformer.transform( - TransformerContext( - path = path, - inputStream = inputStream, - relocators = relocators, - ), + TransformerContext(path = path, inputStream = inputStream, relocators = relocators) ) } return true } private fun FileCopyDetails.writeToZip(entryName: String) { - val entry = zipEntry(entryName, preserveFileTimestamps, lastModified) { - unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() - } + val entry = + zipEntry(entryName, preserveFileTimestamps, lastModified) { + unixMode = UnixStat.FILE_FLAG or permissions.toUnixNumeric() + } zipOutStr.putNextEntry(entry) copyTo(zipOutStr) zipOutStr.closeEntry() @@ -315,13 +332,16 @@ constructor( private val logger = Logging.getLogger(ShadowCopyAction::class.java) private val ZipOutputStream.entries: List - get() = this::class.java.getDeclaredField("entries").apply { isAccessible = true }.get(this).cast() + get() = + this::class.java.getDeclaredField("entries").apply { isAccessible = true }.get(this).cast() /** - * A copy of [org.gradle.api.internal.file.archive.ZipEntryConstants.CONSTANT_TIME_FOR_ZIP_ENTRIES]. + * A copy of + * [org.gradle.api.internal.file.archive.ZipEntryConstants.CONSTANT_TIME_FOR_ZIP_ENTRIES]. * * 1980-02-01 00:00:00 (318182400000). */ - public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis + public val CONSTANT_TIME_FOR_ZIP_ENTRIES: Long = + GregorianCalendar(1980, 1, 1, 0, 0, 0).timeInMillis } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 4977aa1d4..8ec8ba3c0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -62,10 +62,12 @@ import org.gradle.language.base.plugins.LifecycleBasePlugin @CacheableTask public abstract class ShadowJar : Jar() { private val dependencyFilterForMinimize = MinimizeDependencyFilter(project) - private val shadowDependencies = project.provider { - // Find shadow configuration here instead of get, as the ShadowJar tasks could be registered without Shadow plugin applied. - project.configurations.findByName(ShadowBasePlugin.CONFIGURATION_NAME) ?: project.files() - } + private val shadowDependencies = + project.provider { + // Find shadow configuration here instead of get, as the ShadowJar tasks could be registered + // without Shadow plugin applied. + project.configurations.findByName(ShadowBasePlugin.CONFIGURATION_NAME) ?: project.files() + } init { group = LifecycleBasePlugin.BUILD_GROUP @@ -87,46 +89,48 @@ public abstract class ShadowJar : Jar() { * Defaults to `false`. */ @get:Input - @get:Option(option = "minimize-jar", description = "Minimizes the jar by removing unused classes.") + @get:Option( + option = "minimize-jar", + description = "Minimizes the jar by removing unused classes.", + ) public open val minimizeJar: Property = objectFactory.property(false) @get:Classpath - public open val toMinimize: ConfigurableFileCollection = objectFactory.fileCollection { - minimizeJar.map { - if (it) (dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) else emptySet() + public open val toMinimize: ConfigurableFileCollection = + objectFactory.fileCollection { + minimizeJar.map { + if (it) (dependencyFilterForMinimize.resolve(configurations.get()) - apiJars) + else emptySet() + } } - } @get:Classpath - public open val apiJars: ConfigurableFileCollection = objectFactory.fileCollection { - minimizeJar.map { - if (it) UnusedTracker.getApiJarsFromProject(project) else emptySet() + public open val apiJars: ConfigurableFileCollection = + objectFactory.fileCollection { + minimizeJar.map { if (it) UnusedTracker.getApiJarsFromProject(project) else emptySet() } } - } @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) - public open val sourceSetsClassesDirs: ConfigurableFileCollection = objectFactory.fileCollection { - minimizeJar.map { - if (it) { - project.sourceSets.map { sourceSet -> sourceSet.output.classesDirs.filter(File::isDirectory) } - } else { - emptySet() + public open val sourceSetsClassesDirs: ConfigurableFileCollection = + objectFactory.fileCollection { + minimizeJar.map { + if (it) { + project.sourceSets.map { sourceSet -> + sourceSet.output.classesDirs.filter(File::isDirectory) + } + } else { + emptySet() + } } } - } - /** - * [ResourceTransformer]s to be applied in the shadow steps. - */ + /** [ResourceTransformer]s to be applied in the shadow steps. */ @get:Nested public open val transformers: SetProperty = objectFactory.setProperty() - /** - * [Relocator]s to be applied in the shadow steps. - */ - @get:Nested - public open val relocators: SetProperty = objectFactory.setProperty() + /** [Relocator]s to be applied in the shadow steps. */ + @get:Nested public open val relocators: SetProperty = objectFactory.setProperty() /** * The configurations to include dependencies from. @@ -140,13 +144,12 @@ public abstract class ShadowJar : Jar() { public open val dependencyFilter: Property = objectFactory.property(DefaultDependencyFilter(project)) - /** - * Final dependencies to be shadowed. - */ + /** Final dependencies to be shadowed. */ @get:Classpath - public open val includedDependencies: ConfigurableFileCollection = objectFactory.fileCollection { - dependencyFilter.zip(configurations) { df, cs -> df.resolve(cs) } - } + public open val includedDependencies: ConfigurableFileCollection = + objectFactory.fileCollection { + dependencyFilter.zip(configurations) { df, cs -> df.resolve(cs) } + } /** * Enables auto relocation of packages in the dependencies. @@ -165,9 +168,10 @@ public abstract class ShadowJar : Jar() { /** * Enables remapping of Kotlin module metadata (`.kotlin_module`) files. * - * If you enable this option, the Kotlin module metadata file paths and their contents will be relocated if they are - * matched by any of the configured [relocators]. Someone may want to disable this feature and write their own - * [ResourceTransformer]s to handle Kotlin module metadata files in a custom way. + * If you enable this option, the Kotlin module metadata file paths and their contents will be + * relocated if they are matched by any of the configured [relocators]. Someone may want to + * disable this feature and write their own [ResourceTransformer]s to handle Kotlin module + * metadata files in a custom way. * * Defaults to `true`. */ @@ -190,13 +194,14 @@ public abstract class ShadowJar : Jar() { option = "relocation-prefix", description = "Prefix used for auto relocation of packages in the dependencies.", ) - public open val relocationPrefix: Property = objectFactory.property(ShadowBasePlugin.SHADOW) + public open val relocationPrefix: Property = + objectFactory.property(ShadowBasePlugin.SHADOW) /** * Main class attribute to add to manifest. * - * This property will be used as a fallback if there is no explicit `Main-Class` attribute set for the [ShadowJar] - * task or the main [Jar] task. + * This property will be used as a fallback if there is no explicit `Main-Class` attribute set for + * the [ShadowJar] task or the main [Jar] task. * * Defaults to `null`. */ @@ -224,8 +229,8 @@ public abstract class ShadowJar : Jar() { public open val failOnDuplicateEntries: Property = objectFactory.property(false) /** - * Adds the [java.util.jar.Attributes.Name.MULTI_RELEASE] attribute to the manifest of the shadow JAR if any - * dependencies contain the attribute. + * Adds the [java.util.jar.Attributes.Name.MULTI_RELEASE] attribute to the manifest of the shadow + * JAR if any dependencies contain the attribute. * * Defaults to `true`. */ @@ -251,19 +256,24 @@ public abstract class ShadowJar : Jar() { * * This global strategy can be overridden for individual files by using [filesMatching]. * - * The default value is [EXCLUDE]. Different strategies will lead to different results for `foo/bar` files in the JARs to be merged: - * + * The default value is [EXCLUDE]. Different strategies will lead to different results for + * `foo/bar` files in the JARs to be merged: * - [EXCLUDE]: The **first** `foo/bar` file will be included in the final JAR. - * - [FAIL]: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate `foo/bar` files. + * - [FAIL]: **Fail** the build with a `DuplicateFileCopyingException` if there are duplicate + * `foo/bar` files. * - [INCLUDE]: **Duplicate** `foo/bar` entries will be included in the final JAR. - * - [INHERIT]: **Fail** the build with an exception like `Entry .* is a duplicate but no duplicate handling strategy has been set`. - * - [WARN]: **Warn** about duplicates in the build log, this behaves exactly as [INHERIT] otherwise. - * - * **NOTE:** The strategy takes precedence over transforming and relocating. - * Some [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the strategy to - * [EXCLUDE] (the default), as the duplicate resource files fed for them are excluded beforehand. - * Want [ResourceTransformer]s and the strategy to work together? See more details in the - * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * - [INHERIT]: **Fail** the build with an exception like `Entry .* is a duplicate but no + * duplicate handling strategy has been set`. + * - [WARN]: **Warn** about duplicates in the build log, this behaves exactly as [INHERIT] + * otherwise. + * + * **NOTE:** The strategy takes precedence over transforming and relocating. Some + * [ResourceTransformer]s like [ServiceFileTransformer] will not work as expected with setting the + * strategy to [EXCLUDE] (the default), as the duplicate resource files fed for them are excluded + * beforehand. Want [ResourceTransformer]s and the strategy to work together? See more details in + * the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) + * section. * * @see [eachFile] * @see [filesMatching] @@ -273,21 +283,16 @@ public abstract class ShadowJar : Jar() { */ override fun getDuplicatesStrategy(): DuplicatesStrategy = super.getDuplicatesStrategy() - @get:Inject - protected abstract val archiveOperations: ArchiveOperations + @get:Inject protected abstract val archiveOperations: ArchiveOperations - /** - * Enable [minimizeJar] and execute the [action] with the [DependencyFilter] for minimize. - */ + /** Enable [minimizeJar] and execute the [action] with the [DependencyFilter] for minimize. */ @JvmOverloads public open fun minimize(action: Action = Action {}) { minimizeJar.set(true) action.execute(dependencyFilterForMinimize) } - /** - * Extra dependency operations to be applied in the shadow steps. - */ + /** Extra dependency operations to be applied in the shadow steps. */ public open fun dependencies(action: Action) { action.execute(dependencyFilter.get()) } @@ -295,9 +300,10 @@ public abstract class ShadowJar : Jar() { /** * Merge Java services files with [rootPath]. * - * *Warning*: In most cases, this should be used with the correct [getDuplicatesStrategy] to ensure duplicate service - * files are handled properly. See more details in the - * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * *Warning*: In most cases, this should be used with the correct [getDuplicatesStrategy] to + * ensure duplicate service files are handled properly. See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) + * section. * * @see [getDuplicatesStrategy] */ @@ -308,9 +314,10 @@ public abstract class ShadowJar : Jar() { /** * Merge Java services files with [action]. * - * *Warning*: In most cases, this should be used with the correct [getDuplicatesStrategy] to ensure duplicate service - * files are handled properly. See more details in the - * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * *Warning*: In most cases, this should be used with the correct [getDuplicatesStrategy] to + * ensure duplicate service files are handled properly. See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) + * section. * * @see [getDuplicatesStrategy] */ @@ -322,9 +329,10 @@ public abstract class ShadowJar : Jar() { /** * Merge Groovy extension modules (`META-INF/**/org.codehaus.groovy.runtime.ExtensionModule`). * - * *Warning*: In most cases, this should be used with the correct [getDuplicatesStrategy] to ensure duplicate extension module - * files are handled properly. See more details in the - * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * *Warning*: In most cases, this should be used with the correct [getDuplicatesStrategy] to + * ensure duplicate extension module files are handled properly. See more details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) + * section. * * @see [getDuplicatesStrategy] */ @@ -335,22 +343,25 @@ public abstract class ShadowJar : Jar() { /** * Append contents to a resource in the jar. * - * e.g. `append("resources/application.yml", "\n---\n")` for merging `resources/application.yml` files. + * e.g. `append("resources/application.yml", "\n---\n")` for merging `resources/application.yml` + * files. * * @param resourcePath The path to the resource in the jar. - * @param separator The separator to use between the original content and the appended content, defaults to [AppendingTransformer.DEFAULT_SEPARATOR] (`\n`). + * @param separator The separator to use between the original content and the appended content, + * defaults to [AppendingTransformer.DEFAULT_SEPARATOR] (`\n`). */ @JvmOverloads - public open fun append(resourcePath: String, separator: String = AppendingTransformer.DEFAULT_SEPARATOR) { + public open fun append( + resourcePath: String, + separator: String = AppendingTransformer.DEFAULT_SEPARATOR, + ) { transform(AppendingTransformer::class.java) { it.resource.set(resourcePath) it.separator.set(separator) } } - /** - * Relocate classes and resources matching [pattern] to [destination] using [SimpleRelocator]. - */ + /** Relocate classes and resources matching [pattern] to [destination] using [SimpleRelocator]. */ @JvmOverloads public open fun relocate( pattern: String, @@ -361,26 +372,20 @@ public abstract class ShadowJar : Jar() { addRelocator(relocator, action) } - /** - * Relocate classes and resources using a [Relocator]. - */ + /** Relocate classes and resources using a [Relocator]. */ @JvmOverloads public open fun relocate(clazz: Class, action: Action = Action {}) { val relocator = clazz.getDeclaredConstructor().newInstance() addRelocator(relocator, action) } - /** - * Relocate classes and resources using a [Relocator]. - */ + /** Relocate classes and resources using a [Relocator]. */ @JvmOverloads public open fun relocate(relocator: R, action: Action = Action {}) { addRelocator(relocator, action) } - /** - * Relocate classes and resources using a [Relocator]. - */ + /** Relocate classes and resources using a [Relocator]. */ @JvmSynthetic public inline fun relocate(action: Action = Action {}) { relocate(R::class.java, action) @@ -389,37 +394,49 @@ public abstract class ShadowJar : Jar() { /** * Transform resources using a [ResourceTransformer]. * - * *Warning*: Most of the [ResourceTransformer]s should be used with the correct [getDuplicatesStrategy] to ensure - * duplicate resource files are handled properly. See more details in the - * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * *Warning*: Most of the [ResourceTransformer]s should be used with the correct + * [getDuplicatesStrategy] to ensure duplicate resource files are handled properly. See more + * details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) + * section. * * @see [getDuplicatesStrategy] */ @JvmOverloads - public open fun transform(clazz: Class, action: Action = Action {}) { + public open fun transform( + clazz: Class, + action: Action = Action {}, + ) { addTransform(clazz.create(objectFactory), action) } /** * Transform resources using a [ResourceTransformer]. * - * *Warning*: Most of the [ResourceTransformer]s should be used with the correct [getDuplicatesStrategy] to ensure - * duplicate resource files are handled properly. See more details in the - * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * *Warning*: Most of the [ResourceTransformer]s should be used with the correct + * [getDuplicatesStrategy] to ensure duplicate resource files are handled properly. See more + * details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) + * section. * * @see [getDuplicatesStrategy] */ @JvmOverloads - public open fun transform(transformer: T, action: Action = Action {}) { + public open fun transform( + transformer: T, + action: Action = Action {}, + ) { addTransform(transformer, action) } /** * Transform resources using a [ResourceTransformer]. * - * *Warning*: Most of the [ResourceTransformer]s should be used with the correct [getDuplicatesStrategy] to ensure - * duplicate resource files are handled properly. See more details in the - * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) section. + * *Warning*: Most of the [ResourceTransformer]s should be used with the correct + * [getDuplicatesStrategy] to ensure duplicate resource files are handled properly. See more + * details in the + * [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy) + * section. * * @see [getDuplicatesStrategy] */ @@ -436,11 +453,13 @@ public abstract class ShadowJar : Jar() { from(file) } file.extension.equals("aar", ignoreCase = true) -> { - val message = """ + val message = + """ Shadowing AAR file is not supported. Please exclude dependency artifact: $file or use Android Fused Library plugin instead. See https://developer.android.com/build/publish-library/fused-library. - """.trimIndent() + """ + .trimIndent() error(message) } else -> { @@ -456,17 +475,20 @@ public abstract class ShadowJar : Jar() { override fun createCopyAction(): org.gradle.api.internal.file.copy.CopyAction { val zosProvider = { destination: File -> try { - val entryCompressionMethod = when (entryCompression) { - ZipEntryCompression.DEFLATED -> ZipOutputStream.DEFLATED - ZipEntryCompression.STORED -> ZipOutputStream.STORED - } - val stream = if (entryCompressionMethod == ZipOutputStream.STORED) { - ZipOutputStream(destination) - } else { - // Improve performance by avoiding lots of small writes to the file system. - // It is not possible to do this with STORED entries as the implementation requires a RandomAccessFile to update the CRC after write. - ZipOutputStream(destination.outputStream().buffered()) - } + val entryCompressionMethod = + when (entryCompression) { + ZipEntryCompression.DEFLATED -> ZipOutputStream.DEFLATED + ZipEntryCompression.STORED -> ZipOutputStream.STORED + } + val stream = + if (entryCompressionMethod == ZipOutputStream.STORED) { + ZipOutputStream(destination) + } else { + // Improve performance by avoiding lots of small writes to the file system. + // It is not possible to do this with STORED entries as the implementation requires a + // RandomAccessFile to update the CRC after write. + ZipOutputStream(destination.outputStream().buffered()) + } stream.apply { setUseZip64(if (isZip64) Zip64Mode.AsNeeded else Zip64Mode.Never) setMethod(entryCompressionMethod) @@ -475,19 +497,19 @@ public abstract class ShadowJar : Jar() { throw IOException("Unable to create ZIP output stream for file $destination.", e) } } - val unusedClasses = if (minimizeJar.get()) { - val unusedTracker = UnusedTracker( - sourceSetsClassesDirs = sourceSetsClassesDirs.files, - classJars = apiJars, - toMinimize = toMinimize, - ) - includedDependencies.files.forEach { - unusedTracker.addDependency(it) + val unusedClasses = + if (minimizeJar.get()) { + val unusedTracker = + UnusedTracker( + sourceSetsClassesDirs = sourceSetsClassesDirs.files, + classJars = apiJars, + toMinimize = toMinimize, + ) + includedDependencies.files.forEach { unusedTracker.addDependency(it) } + unusedTracker.findUnused() + } else { + emptySet() } - unusedTracker.findUnused() - } else { - emptySet() - } @Suppress("DEPRECATION") return ShadowCopyAction( zipFile = archiveFile.get().asFile, @@ -515,7 +537,9 @@ public abstract class ShadowJar : Jar() { private val packageRelocators: List get() { if (enableAutoRelocation.get()) { - logger.info("Adding auto relocation packages in the dependencies with prefix '${relocationPrefix.get()}'.") + logger.info( + "Adding auto relocation packages in the dependencies with prefix '${relocationPrefix.get()}'." + ) } else { logger.info("Skipping package relocators as auto relocation is disabled.") return emptyList() @@ -523,7 +547,9 @@ public abstract class ShadowJar : Jar() { val prefix = relocationPrefix.get() return includedDependencies.files.flatMap { file -> JarFile(file).use { jarFile -> - jarFile.entries().toList() + jarFile + .entries() + .toList() .filter { it.name.endsWith(".class") && it.name != "module-info.class" } .map { it.name.substringBeforeLast('/').replace('/', '.') } .toSet() @@ -536,14 +562,20 @@ public abstract class ShadowJar : Jar() { val mainClassValue = mainClass.orNull when { manifest.attributes.contains(mainClassAttributeKey) -> { - logger.info("Skipping adding $mainClassAttributeKey attribute to the manifest as it is already set.") + logger.info( + "Skipping adding $mainClassAttributeKey attribute to the manifest as it is already set." + ) } mainClassValue.isNullOrEmpty() -> { - logger.info("Skipping adding $mainClassAttributeKey attribute to the manifest as it is empty.") + logger.info( + "Skipping adding $mainClassAttributeKey attribute to the manifest as it is empty." + ) } else -> { manifest.attributes[mainClassAttributeKey] = mainClassValue - logger.info("Adding $mainClassAttributeKey attribute to the manifest with value '$mainClassValue'.") + logger.info( + "Adding $mainClassAttributeKey attribute to the manifest with value '$mainClassValue'." + ) } } @@ -555,22 +587,28 @@ public abstract class ShadowJar : Jar() { } if (addMultiReleaseAttribute.get()) { - logger.info("Adding $multiReleaseAttributeKey attribute to the manifest if any dependencies contain it.") + logger.info( + "Adding $multiReleaseAttributeKey attribute to the manifest if any dependencies contain it." + ) } else { - logger.info("Skipping adding $multiReleaseAttributeKey attribute to the manifest as it is disabled.") + logger.info( + "Skipping adding $multiReleaseAttributeKey attribute to the manifest as it is disabled." + ) return } - val includeMultiReleaseAttr = includedDependencies.files.any { - try { - JarFile(it).use { jarFile -> - // Manifest might be null or the attribute name is invalid, or any other case. - runCatching { jarFile.manifest.mainAttributes.getValue(multiReleaseAttributeKey) }.getOrNull() - } == "true" - } catch (_: IOException) { - // If the jar file is not valid, ignore it. - false + val includeMultiReleaseAttr = + includedDependencies.files.any { + try { + JarFile(it).use { jarFile -> + // Manifest might be null or the attribute name is invalid, or any other case. + runCatching { jarFile.manifest.mainAttributes.getValue(multiReleaseAttributeKey) } + .getOrNull() + } == "true" + } catch (_: IOException) { + // If the jar file is not valid, ignore it. + false + } } - } if (includeMultiReleaseAttr) { manifest.attributes[multiReleaseAttributeKey] = true } @@ -587,32 +625,36 @@ public abstract class ShadowJar : Jar() { jarTask: TaskProvider, action: Action, ): TaskProvider { - return tasks.register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> - 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", - ) - - task.manifest = DefaultInheritManifest( - project, - @Suppress("EagerGradleConfiguration") // The ctor doesn't support Provider. - jarTask.get().manifest, - ) - - action.execute(task) - }.also { task -> - // Can't use `named` directly as the task is optional or may not exist when the plugin is applied. - // Using Spec applies the action to the task if it is added later. - tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME::equals).configureEach { - it.dependsOn(task) + return tasks + .register(SHADOW_JAR_TASK_NAME, ShadowJar::class.java) { task -> + 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", + ) + + task.manifest = + DefaultInheritManifest( + project, + @Suppress("EagerGradleConfiguration") // The ctor doesn't support Provider. + jarTask.get().manifest, + ) + + action.execute(task) + } + .also { task -> + // Can't use `named` directly as the task is optional or may not exist when the plugin is + // applied. + // Using Spec applies the action to the task if it is added later. + tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME::equals).configureEach { + it.dependsOn(task) + } } - } } } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt index f28ec92b3..3a9826114 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformer.kt @@ -5,19 +5,19 @@ import org.gradle.api.tasks.util.PatternSet /** * Prevents duplicate copies of the license. * - * Modified from [org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformer.java). * * @author John Engelman */ @CacheableTransformer -public open class ApacheLicenseResourceTransformer @JvmOverloads constructor( - patternSet: PatternSet = PatternSet() - .apply { isCaseSensitive = false } - .include( - LICENSE_PATH, - LICENSE_TXT_PATH, - LICENSE_MD_PATH, - ), +public open class ApacheLicenseResourceTransformer +@JvmOverloads +constructor( + patternSet: PatternSet = + PatternSet() + .apply { isCaseSensitive = false } + .include(LICENSE_PATH, LICENSE_TXT_PATH, LICENSE_MD_PATH) ) : PatternFilterableResourceTransformer(patternSet = patternSet) { private companion object { private const val LICENSE_PATH = "META-INF/LICENSE" diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt index 95e04b0e0..2276eff33 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformer.kt @@ -17,7 +17,8 @@ import org.gradle.api.tasks.util.PatternSet /** * Merges `META-INF/NOTICE.TXT` files. * - * Modified from [org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformer.java). * * @author John Engelman */ @@ -28,51 +29,50 @@ public open class ApacheNoticeResourceTransformer( ) : PatternFilterableResourceTransformer(patternSet) { private val entries = mutableSetOf() private val organizationEntries = mutableMapOf>() - private inline val charset get() = Charset.forName(charsetName.get()) + private inline val charset + get() = Charset.forName(charsetName.get()) - /** - * Fallback [copyright] as the [Property] value can't be changed in execution phase. - */ + /** Fallback [copyright] as the [Property] value can't be changed in execution phase. */ private var fallbackCopyright: String? = null - @get:Input - public open val projectName: Property = objectFactory.property("") + @get:Input public open val projectName: Property = objectFactory.property("") - @get:Input - public open val addHeader: Property = objectFactory.property(true) + @get:Input public open val addHeader: Property = objectFactory.property(true) @get:Input - public open val preamble1: Property = objectFactory.property( - """ + public open val preamble1: Property = + objectFactory.property( + """ // ------------------------------------------------------------------ // NOTICE file corresponding to the section 4d of The Apache License, // Version 2.0, in this case for - """.trimIndent() + " ", // The space is important for formatting. - ) + """ + .trimIndent() + " " // The space is important for formatting. + ) @get:Input - public open val preamble2: Property = objectFactory.property( - "\n// ------------------------------------------------------------------\n", - ) + public open val preamble2: Property = + objectFactory.property( + "\n// ------------------------------------------------------------------\n" + ) @get:Input - public open val preamble3: Property = objectFactory.property("This product includes software developed at\n") + public open val preamble3: Property = + objectFactory.property("This product includes software developed at\n") @get:Input - public open val organizationName: Property = objectFactory.property("The Apache Software Foundation") + public open val organizationName: Property = + objectFactory.property("The Apache Software Foundation") @get:Input - public open val organizationURL: Property = objectFactory.property("https://www.apache.org/") + public open val organizationURL: Property = + objectFactory.property("https://www.apache.org/") - @get:Input - public open val inceptionYear: Property = objectFactory.property("2006") + @get:Input public open val inceptionYear: Property = objectFactory.property("2006") - @get:Input - public open val copyright: Property = objectFactory.property("") + @get:Input public open val copyright: Property = objectFactory.property("") - /** - * The file encoding of the `NOTICE` file. - */ + /** The file encoding of the `NOTICE` file. */ @get:Input public open val charsetName: Property = objectFactory.property(Charsets.UTF_8.name()) @@ -81,19 +81,17 @@ public open class ApacheNoticeResourceTransformer( * * Defaults to `META-INF/NOTICE`. */ - @get:Input - public open val outputPath: Property = objectFactory.property(NOTICE_PATH) + @get:Input public open val outputPath: Property = objectFactory.property(NOTICE_PATH) @Inject - public constructor(objectFactory: ObjectFactory) : this( + public constructor( + objectFactory: ObjectFactory + ) : this( objectFactory, - patternSet = PatternSet() - .apply { isCaseSensitive = false } - .include( - NOTICE_PATH, - NOTICE_TXT_PATH, - NOTICE_MD_PATH, - ), + patternSet = + PatternSet() + .apply { isCaseSensitive = false } + .include(NOTICE_PATH, NOTICE_TXT_PATH, NOTICE_MD_PATH), ) override fun transform(context: TransformerContext) { @@ -107,9 +105,10 @@ public open class ApacheNoticeResourceTransformer( val inceptionYear = inceptionYear.get() if (entries.isEmpty()) { - val year = SimpleDateFormat("yyyy", Locale.US).format(Date()).let { - if (inceptionYear != it) "$inceptionYear-$it" else it - } + val year = + SimpleDateFormat("yyyy", Locale.US).format(Date()).let { + if (inceptionYear != it) "$inceptionYear-$it" else it + } // Add headers. if (addHeader) { entries.add("$preamble1$projectName$preamble2") @@ -132,7 +131,10 @@ public open class ApacheNoticeResourceTransformer( if (trimmedLine.isNotEmpty()) { if (trimmedLine.startsWith("- ")) { // resource-bundle 1.3 mode. - if (lineCount == 1 && sb.toString().contains("This product includes/uses software(s) developed by")) { + if ( + lineCount == 1 && + sb.toString().contains("This product includes/uses software(s) developed by") + ) { currentOrg = organizationEntries.getOrPut(sb.toString().trim()) { TreeSet() } sb.setLength(0) } else if (sb.isNotEmpty() && currentOrg != null) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt index b65ebd2fd..7e29c4ceb 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformer.kt @@ -13,23 +13,23 @@ import org.gradle.api.tasks.Input /** * A resource processor that appends content for a resource, separated by a newline. * - * Modified from [org.apache.maven.plugins.shade.resource.AppendingTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/AppendingTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.AppendingTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/AppendingTransformer.java). * * @author John Engelman */ @CacheableTransformer -public open class AppendingTransformer @Inject constructor( - final override val objectFactory: ObjectFactory, -) : ResourceTransformer { - @Suppress("ktlint:standard:backing-property-naming") - private var _data: ByteArrayOutputStream? = null // It's nullable to allow lazy initialization to support CC. - private inline val data get() = _data ?: ByteArrayOutputStream().also { _data = it } +public open class AppendingTransformer +@Inject +constructor(final override val objectFactory: ObjectFactory) : ResourceTransformer { + private var _data: ByteArrayOutputStream? = + null // It's nullable to allow lazy initialization to support CC. + private inline val data + get() = _data ?: ByteArrayOutputStream().also { _data = it } - @get:Input - public open val resource: Property = objectFactory.property("") + @get:Input public open val resource: Property = objectFactory.property("") - @get:Input - public open val separator: Property = objectFactory.property(DEFAULT_SEPARATOR) + @get:Input public open val separator: Property = objectFactory.property(DEFAULT_SEPARATOR) override fun canTransformResource(element: FileTreeElement): Boolean { return resource.get().equals(element.path, ignoreCase = true) @@ -38,7 +38,8 @@ public open class AppendingTransformer @Inject constructor( override fun transform(context: TransformerContext) { data.let { if (it.size() > 0) { - // Append the separator before the new content to ensure the separator is not at the end of the file. + // Append the separator before the new content to ensure the separator is not at the end of + // the file. it.write(separator.get().toByteArray()) } context.inputStream.copyTo(it) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt index a28e0e68e..b36860de0 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformer.kt @@ -17,7 +17,8 @@ import org.gradle.api.tasks.Internal /** * A resource processor that aggregates plexus `components.xml` files. * - * Modified from [org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformer.java). * * @author John Engelman */ @@ -46,49 +47,45 @@ public open class ComponentsXmlResourceTransformer : ResourceTransformer { } override fun transform(context: TransformerContext) { - val newDom = try { - val bis = object : BufferedInputStream(context.inputStream) { - @Throws(IOException::class) - override fun close() { - // Leave ZIP open. - } + val newDom = + try { + val bis = + object : BufferedInputStream(context.inputStream) { + @Throws(IOException::class) + override fun close() { + // Leave ZIP open. + } + } + Xpp3DomBuilder.build(XmlStreamReader(bis)) + } catch (e: Exception) { + throw IOException("Error parsing components.xml in ${context.inputStream}", e) } - Xpp3DomBuilder.build(XmlStreamReader(bis)) - } catch (e: Exception) { - throw IOException("Error parsing components.xml in ${context.inputStream}", e) - } // Only try to merge in components if there are some elements in the component-set. if (newDom.getChild("components") == null) return val children = newDom.getChild("components").getChildren("component") for (component in children) { - val role = getValue(component, "role").let { - context.relocators.relocateClass(it) - } + val role = getValue(component, "role").let { context.relocators.relocateClass(it) } setValue(component, "role", role) val roleHint = getValue(component, "role-hint") - val impl = getValue(component, "implementation").let { - context.relocators.relocateClass(it) - } + val impl = getValue(component, "implementation").let { context.relocators.relocateClass(it) } setValue(component, "implementation", impl) val key = "$role:$roleHint" - // TODO: use the tools in Plexus to merge these properly. For now, I just need an all-or-nothing. + // TODO: use the tools in Plexus to merge these properly. For now, I just need an + // all-or-nothing. // Configuration carry over. - components[key]?.getChild("configuration")?.let { - component.addChild(it) - } + components[key]?.getChild("configuration")?.let { component.addChild(it) } val requirements = component.getChild("requirements") if (requirements != null && requirements.childCount > 0) { for (r in requirements.childCount - 1 downTo 0) { val requirement = requirements.getChild(r) - val requiredRole = getValue(requirement, "role").let { - context.relocators.relocateClass(it) - } + val requiredRole = + getValue(requirement, "role").let { context.relocators.relocateClass(it) } setValue(requirement, "role", requiredRole) } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt index 3c5498a84..61d95e66d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformer.kt @@ -17,28 +17,25 @@ import org.gradle.api.tasks.util.PatternSet * Multiple files with the same path but different content lead to an error. * * Some scenarios for duplicate resources in a shadow jar: + * - Duplicate `.class` files Having duplicate `.class` files with different content is a situation + * indicating that the resulting jar is built with _incompatible_ classes, likely leading to + * issues during runtime. This situation can happen when one dependency is (also) included in an + * uber jar. + * - Duplicate `META-INF///pom.properties`/`xml` files. Some dependencies + * contain shaded variants of other dependencies. Tools that inspect jar files to extract the + * included dependencies, for example, for license auditing use cases or tools that collect + * information of all included dependencies, may rely on these files. Hence, it is desirable to + * retain the duplicate resource `pom.properties`/`xml` resources. * - * - Duplicate `.class` files - * Having duplicate `.class` files with different content is a situation indicating that the resulting jar is - * built with _incompatible_ classes, likely leading to issues during runtime. - * This situation can happen when one dependency is (also) included in an uber jar. - * - * - Duplicate `META-INF///pom.properties`/`xml` files. - * Some dependencies contain shaded variants of other dependencies. - * Tools that inspect jar files to extract the included dependencies, for example, for license auditing - * use cases or tools that collect information of all included dependencies, may rely on these files. - * Hence, it is desirable to retain the duplicate resource `pom.properties`/`xml` resources. - * - * [DeduplicatingResourceTransformer] checks all entries in the resulting jar. - * It is generally not recommended to use any of the [include] configuration functions. + * [DeduplicatingResourceTransformer] checks all entries in the resulting jar. It is generally not + * recommended to use any of the [include] configuration functions. * * There are reasons to retain duplicate resources with different contents in the resulting jar. * This can be achieved with the [exclude] configuration functions. * * To exclude a path or pattern from being deduplicated, for example, legit - * `META-INF///pom.properties`/`xml`, configure the transformer with an exclusion - * like the following: - * + * `META-INF///pom.properties`/`xml`, configure the transformer with an + * exclusion like the following: * ```kotlin * tasks.shadowJar { * transform(DeduplicatingResourceTransformer::class.java) { @@ -52,27 +49,25 @@ import org.gradle.api.tasks.util.PatternSet * *Tip*: the [FindResourceInClasspath] convenience task can be used to find resources in a Gradle * classpath/configuration. * - * *Warning* Do **not** combine [PreserveFirstFoundResourceTransformer] with this transformer, - * as they handle duplicates differently and combining them would lead to redundant or unexpected behavior. + * *Warning* Do **not** combine [PreserveFirstFoundResourceTransformer] with this transformer, as + * they handle duplicates differently and combining them would lead to redundant or unexpected + * behavior. */ @CacheableTransformer public open class DeduplicatingResourceTransformer( final override val objectFactory: ObjectFactory, patternSet: PatternSet, ) : PatternFilterableResourceTransformer(patternSet) { - @get:Internal - internal val sources: MutableMap = mutableMapOf() + @get:Internal internal val sources: MutableMap = mutableMapOf() - @Inject - public constructor(objectFactory: ObjectFactory) : this(objectFactory, PatternSet()) + @Inject public constructor(objectFactory: ObjectFactory) : this(objectFactory, PatternSet()) override fun canTransformResource(element: FileTreeElement): Boolean { val file = element.file val hash = file.sha256Hex() - val pathInfos = sources.computeIfAbsent(element.path) { - PathInfos(patternSpec.isSatisfiedBy(element)) - } + val pathInfos = + sources.computeIfAbsent(element.path) { PathInfos(patternSpec.isSatisfiedBy(element)) } val retainInOutput = pathInfos.addFile(hash, file) return !retainInOutput @@ -85,13 +80,13 @@ public open class DeduplicatingResourceTransformer( if (duplicatePaths.isNotEmpty()) { val message = buildString { - append("Found ${duplicatePaths.size} path duplicate(s) with different content in the shadowed JAR:\n") + append( + "Found ${duplicatePaths.size} path duplicate(s) with different content in the shadowed JAR:\n" + ) duplicatePaths.forEach { (path, infos) -> append(" * $path\n") infos.filesPerHash.forEach { (hash, files) -> - files.forEach { file -> - append(" * ${file.path} (SHA256: $hash)\n") - } + files.forEach { file -> append(" * ${file.path} (SHA256: $hash)\n") } } } } @@ -99,9 +94,10 @@ public open class DeduplicatingResourceTransformer( } } - internal fun duplicateContentViolations(): Map = sources.filter { (_, pathInfos) -> - pathInfos.failOnDuplicateContent && pathInfos.uniqueContentCount() > 1 - } + internal fun duplicateContentViolations(): Map = + sources.filter { (_, pathInfos) -> + pathInfos.failOnDuplicateContent && pathInfos.uniqueContentCount() > 1 + } internal data class PathInfos(val failOnDuplicateContent: Boolean) { val filesPerHash: MutableMap> = mutableMapOf() @@ -118,9 +114,7 @@ public open class DeduplicatingResourceTransformer( internal companion object { fun File.sha256Hex(): String { try { - return inputStream().use { - DigestUtils.sha256Hex(it) - } + return inputStream().use { DigestUtils.sha256Hex(it) } } catch (e: Exception) { throw RuntimeException("Failed to read data or calculate hash for $this", e) } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt index 308913552..c7e459edb 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DontIncludeResourceTransformer.kt @@ -13,16 +13,17 @@ import org.gradle.api.tasks.Input * * You can also use [ShadowJar.exclude] instead. * - * Modified from [org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/DontIncludeResourceTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.DontIncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/DontIncludeResourceTransformer.java). * * @author John Engelman */ @CacheableTransformer -public open class DontIncludeResourceTransformer @Inject constructor( - final override val objectFactory: ObjectFactory, -) : ResourceTransformer by ResourceTransformer.Companion { - @get:Input - public open val resource: Property = objectFactory.property("") +public open class DontIncludeResourceTransformer +@Inject +constructor(final override val objectFactory: ObjectFactory) : + ResourceTransformer by ResourceTransformer.Companion { + @get:Input public open val resource: Property = objectFactory.property("") override fun canTransformResource(element: FileTreeElement): Boolean { return resource.get().isNotEmpty() && element.path.endsWith(resource.get()) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt index ea7ef9b6c..bce9df28e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/GroovyExtensionModuleTransformer.kt @@ -11,18 +11,20 @@ import org.gradle.api.file.FileTreeElement * Aggregate Apache Groovy extension modules descriptors. * * Resource transformer that merges Groovy extension module descriptor files into a single file. - * Groovy extension module descriptor files have the name org.codehaus.groovy.runtime.ExtensionModule - * and live in the META-INF/services (Groovy up to 2.4) or META-INF/groovy (Groovy 2.5+) directory. - * See [GROOVY-8480](https://issues.apache.org/jira/browse/GROOVY-8480) for more details of the change. + * Groovy extension module descriptor files have the name + * org.codehaus.groovy.runtime.ExtensionModule and live in the META-INF/services (Groovy up to 2.4) + * or META-INF/groovy (Groovy 2.5+) directory. See + * [GROOVY-8480](https://issues.apache.org/jira/browse/GROOVY-8480) for more details of the change. * - * If there are several descriptor files spread across many JARs the individual - * entries will be merged into a single descriptor file which will be - * packaged into the resultant JAR produced by the shadowing process. - * It will live in the legacy directory (META-INF/services) if all the processed descriptor - * files came from the legacy location, otherwise it will be written into the now standard location (META-INF/groovy). - * Note that certain JDK9+ tooling will break when using the legacy location. + * If there are several descriptor files spread across many JARs the individual entries will be + * merged into a single descriptor file which will be packaged into the resultant JAR produced by + * the shadowing process. It will live in the legacy directory (META-INF/services) if all the + * processed descriptor files came from the legacy location, otherwise it will be written into the + * now standard location (META-INF/groovy). Note that certain JDK9+ tooling will break when using + * the legacy location. * - * Related to [org.apache.maven.plugins.shade.resource.GroovyResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java). + * Related to + * [org.apache.maven.plugins.shade.resource.GroovyResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/GroovyResourceTransformer.java). */ @CacheableTransformer public open class GroovyExtensionModuleTransformer : ResourceTransformer { @@ -30,7 +32,8 @@ public open class GroovyExtensionModuleTransformer : ResourceTransformer { override fun canTransformResource(element: FileTreeElement): Boolean { val path = element.path - return path == PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR || path == PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR + return path == PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR || + path == PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR } override fun transform(context: TransformerContext) { @@ -38,18 +41,16 @@ public open class GroovyExtensionModuleTransformer : ResourceTransformer { props.load(context.inputStream) props.forEach { (key, value) -> when (key as String) { - KEY_MODULE_NAME -> handle(key, value as String) { - module.setProperty(key, MERGED_MODULE_NAME) - } - KEY_MODULE_VERSION -> handle(key, value as String) { - module.setProperty(key, MERGED_MODULE_VERSION) - } + KEY_MODULE_NAME -> + handle(key, value as String) { module.setProperty(key, MERGED_MODULE_NAME) } + KEY_MODULE_VERSION -> + handle(key, value as String) { module.setProperty(key, MERGED_MODULE_VERSION) } KEY_EXTENSION_CLASSES, - KEY_STATIC_EXTENSION_CLASSES, - -> { - val relocatedClasses = (value as String).split(',').joinToString(",") { className -> - context.relocators.relocateClass(className) - } + KEY_STATIC_EXTENSION_CLASSES -> { + val relocatedClasses = + (value as String).split(',').joinToString(",") { className -> + context.relocators.relocateClass(className) + } handle(key, relocatedClasses) { existingValue -> module.setProperty(key, "$existingValue,$relocatedClasses") } @@ -62,9 +63,7 @@ public open class GroovyExtensionModuleTransformer : ResourceTransformer { override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { os.putNextEntry(zipEntry(PATH_GROOVY_EXTENSION_MODULE_DESCRIPTOR, preserveFileTimestamps)) - module.inputStream().use { - it.copyTo(os) - } + module.inputStream().use { it.copyTo(os) } os.closeEntry() } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt index bfaf753bf..efb96be76 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/IncludeResourceTransformer.kt @@ -18,29 +18,28 @@ import org.gradle.api.tasks.PathSensitivity * * You can also use [ShadowJar.from] instead. * - * Modified from [org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/IncludeResourceTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.IncludeResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/IncludeResourceTransformer.java). * * @author John Engelman */ @CacheableTransformer -public open class IncludeResourceTransformer @Inject constructor( - final override val objectFactory: ObjectFactory, -) : ResourceTransformer by ResourceTransformer.Companion { +public open class IncludeResourceTransformer +@Inject +constructor(final override val objectFactory: ObjectFactory) : + ResourceTransformer by ResourceTransformer.Companion { @get:InputFile @get:PathSensitive(PathSensitivity.NONE) public open val file: RegularFileProperty = objectFactory.fileProperty() - @get:Input - public open val resource: Property = objectFactory.property() + @get:Input public open val resource: Property = objectFactory.property() override fun hasTransformedResource(): Boolean = file.get().asFile.exists() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { os.putNextEntry(zipEntry(resource.get(), preserveFileTimestamps)) - file.get().asFile.inputStream().use { inputStream -> - inputStream.copyTo(os) - } + file.get().asFile.inputStream().use { inputStream -> inputStream.copyTo(os) } os.closeEntry() } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt index e3ec52983..3408a25f3 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformer.kt @@ -17,21 +17,18 @@ import org.apache.tools.zip.ZipOutputStream import org.gradle.api.file.FileTreeElement /** - * Modified from [org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer.java](https://github.com/apache/logging-log4j-transform/blob/main/log4j-transform-maven-shade-plugin-extensions/src/main/java/org/apache/logging/log4j/maven/plugins/shade/transformer/Log4j2PluginCacheFileTransformer.java). + * Modified from + * [org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformer.java](https://github.com/apache/logging-log4j-transform/blob/main/log4j-transform-maven-shade-plugin-extensions/src/main/java/org/apache/logging/log4j/maven/plugins/shade/transformer/Log4j2PluginCacheFileTransformer.java). * * @author Paul Nelson Baker * @author John Engelman */ @CacheableTransformer public open class Log4j2PluginsCacheFileTransformer : ResourceTransformer { - /** - * Log4j config files to share across the transformation stages. - */ + /** Log4j config files to share across the transformation stages. */ private val tempFiles = mutableListOf() - /** - * [Relocator] instances to share across the transformation stages. - */ + /** [Relocator] instances to share across the transformation stages. */ private val tempRelocators = mutableListOf() override fun canTransformResource(element: FileTreeElement): Boolean { @@ -45,9 +42,7 @@ public open class Log4j2PluginsCacheFileTransformer : ResourceTransformer { tempRelocators.addAll(context.relocators) } - /** - * @return `true` if any dat file collected. - */ + /** @return `true` if any dat file collected. */ override fun hasTransformedResource(): Boolean = tempFiles.isNotEmpty() override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt index 2b3d13dd9..ab8c2d70f 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestAppenderTransformer.kt @@ -14,17 +14,18 @@ import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Input /** - * A resource processor that can append arbitrary attributes to the first MANIFEST.MF - * that is found in the set of JARs being processed. The attributes are appended in - * the specified order, and duplicates are allowed. + * A resource processor that can append arbitrary attributes to the first MANIFEST.MF that is found + * in the set of JARs being processed. The attributes are appended in the specified order, and + * duplicates are allowed. * * Modified from [ManifestResourceTransformer]. + * * @author Chris Rankin */ @CacheableTransformer -public open class ManifestAppenderTransformer @Inject constructor( - final override val objectFactory: ObjectFactory, -) : ResourceTransformer { +public open class ManifestAppenderTransformer +@Inject +constructor(final override val objectFactory: ObjectFactory) : ResourceTransformer { private var manifestContents = ByteArray(0) @get:Input diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt index 1dc654593..21f2ff859 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ManifestResourceTransformer.kt @@ -18,24 +18,24 @@ import org.gradle.api.provider.Property import org.gradle.api.tasks.Input /** - * A resource processor that allows the arbitrary addition of attributes to - * the first MANIFEST.MF that is found in the set of JARs being processed, or - * to a newly created manifest for the shaded JAR. + * A resource processor that allows the arbitrary addition of attributes to the first MANIFEST.MF + * that is found in the set of JARs being processed, or to a newly created manifest for the shaded + * JAR. * - * Modified from [org.apache.maven.plugins.shade.resource.ManifestResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ManifestResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java). * * @author Jason van Zyl * @author John Engelman */ @CacheableTransformer -public open class ManifestResourceTransformer @Inject constructor( - final override val objectFactory: ObjectFactory, -) : ResourceTransformer { +public open class ManifestResourceTransformer +@Inject +constructor(final override val objectFactory: ObjectFactory) : ResourceTransformer { private var manifestDiscovered = false private var manifest: Manifest? = null - @get:Input - public open val mainClass: Property = objectFactory.property("") + @get:Input public open val mainClass: Property = objectFactory.property("") @get:Input public open val manifestEntries: MapProperty = objectFactory.mapProperty() @@ -45,8 +45,10 @@ public open class ManifestResourceTransformer @Inject constructor( } override fun transform(context: TransformerContext) { - // We just want to take the first manifest we come across as that's our project's manifest. This is the behavior - // now which is situational at best. Right now there is no context passed in with the processing so we cannot + // We just want to take the first manifest we come across as that's our project's manifest. This + // is the behavior + // now which is situational at best. Right now there is no context passed in with the processing + // so we cannot // tell what artifact is being processed. if (!manifestDiscovered) { try { @@ -67,12 +69,8 @@ public open class ManifestResourceTransformer @Inject constructor( } val attributes = manifest!!.mainAttributes - mainClass.get().takeIf(CharSequence::isNotEmpty)?.let { - attributes[mainClassAttributeKey] = it - } - manifestEntries.get().forEach { (key, value) -> - attributes[JarAttribute.Name(key)] = value - } + mainClass.get().takeIf(CharSequence::isNotEmpty)?.let { attributes[mainClassAttributeKey] = it } + manifestEntries.get().forEach { (key, value) -> attributes[JarAttribute.Name(key)] = value } os.putNextEntry(zipEntry(JarFile.MANIFEST_NAME, preserveFileTimestamps)) manifest!!.write(os) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt index 0f895c5e4..7abd3af79 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/MergeLicenseResourceTransformer.kt @@ -19,11 +19,10 @@ import org.gradle.api.tasks.util.PatternSet /** * Generates a license file using the configured license text source. * - * An optional `SPDX-License-Identifier` can be placed in front of the license text to avoid ambiguous - * license detection by license-detection-tools. + * An optional `SPDX-License-Identifier` can be placed in front of the license text to avoid + * ambiguous license detection by license-detection-tools. * * License texts found in the file names: - * * - `META-INF/LICENSE` * - `META-INF/LICENSE.txt` * - `META-INF/LICENSE.md` @@ -35,16 +34,15 @@ import org.gradle.api.tasks.util.PatternSet * * To exclude these defaults, add [exclude]s to the transformer configuration. * - * Use the [org.gradle.api.tasks.util.PatternFilterable] functions to specify a different set of files to include, - * the paths mentioned above are then not considered unless explicitly included. + * Use the [org.gradle.api.tasks.util.PatternFilterable] functions to specify a different set of + * files to include, the paths mentioned above are then not considered unless explicitly included. */ @CacheableTransformer public open class MergeLicenseResourceTransformer( final override val objectFactory: ObjectFactory, patternSet: PatternSet, ) : PatternFilterableResourceTransformer(patternSet) { - @get:Internal - internal val elements: MutableSet = LinkedHashSet() + @get:Internal internal val elements: MutableSet = LinkedHashSet() /** Path to write the aggregated license file to. Defaults to `META-INF/LICENSE`. */ @get:Input @@ -68,36 +66,38 @@ public open class MergeLicenseResourceTransformer( * Separator between the project's license text and license texts from the included dependencies. */ @get:Input - public open val firstSeparator: Property = objectFactory.property( - """ + public open val firstSeparator: Property = + objectFactory.property( + """ | |${"-".repeat(120)} | |This artifact includes dependencies with the following licenses: |---------------------------------------------------------------- - | - """.trimMargin(), - ) + |""" + .trimMargin() + ) - /** - * Separator between included dependency license texts. - */ + /** Separator between included dependency license texts. */ @get:Input public open val separator: Property = objectFactory.property("\n${"-".repeat(120)}\n") @Inject - public constructor(objectFactory: ObjectFactory) : this( + public constructor( + objectFactory: ObjectFactory + ) : this( objectFactory, - patternSet = PatternSet().apply { - include( - "META-INF/LICENSE", - "META-INF/LICENSE.txt", - "META-INF/LICENSE.md", - "LICENSE", - "LICENSE.txt", - "LICENSE.md", - ) - }, + patternSet = + PatternSet().apply { + include( + "META-INF/LICENSE", + "META-INF/LICENSE.txt", + "META-INF/LICENSE.md", + "LICENSE", + "LICENSE.txt", + "LICENSE.md", + ) + }, ) override fun transform(context: TransformerContext) { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt index 17945fd8c..5c943d62e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PatternFilterableResourceTransformer.kt @@ -14,9 +14,8 @@ import org.gradle.api.tasks.util.PatternSet * @param patternSet The [PatternSet] used for filtering resources. */ public abstract class PatternFilterableResourceTransformer( - @Internal public val patternSet: PatternSet, -) : ResourceTransformer by ResourceTransformer.Companion, - PatternFilterable by patternSet { + @Internal public val patternSet: PatternSet +) : ResourceTransformer by ResourceTransformer.Companion, PatternFilterable by patternSet { @get:Internal protected val patternSpec: Spec by unsafeLazy { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt index a8b6527f8..915f2f360 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PreserveFirstFoundResourceTransformer.kt @@ -15,16 +15,17 @@ import org.gradle.api.tasks.util.PatternSet /** * A resource processor that preserves the first resource matched and excludes all others. * - * This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the default behavior) and - * want to ensure that only the first found resource is included in the final JAR. If there are multiple resources with - * the same path in a project and its dependencies, the first one found should be the project's. + * This is useful when you set `shadowJar.duplicatesStrategy = DuplicatesStrategy.INCLUDE` (the + * default behavior) and want to ensure that only the first found resource is included in the final + * JAR. If there are multiple resources with the same path in a project and its dependencies, the + * first one found should be the project's. * - * This transformer deduplicates included resources based on the path name. - * See [DeduplicatingResourceTransformer] for a transformer that deduplicates based on the paths and contents of - * the resources. + * This transformer deduplicates included resources based on the path name. See + * [DeduplicatingResourceTransformer] for a transformer that deduplicates based on the paths and + * contents of the resources. * - * *Warning* Do **not** combine [DeduplicatingResourceTransformer] with this transformer, - * as they handle duplicates differently and combining them would lead to redundant or unexpected behavior. + * *Warning* Do **not** combine [DeduplicatingResourceTransformer] with this transformer, as they + * handle duplicates differently and combining them would lead to redundant or unexpected behavior. * * @see [DuplicatesStrategy] * @see [ShadowJar.getDuplicatesStrategy] @@ -34,20 +35,15 @@ public open class PreserveFirstFoundResourceTransformer( final override val objectFactory: ObjectFactory, patternSet: PatternSet, ) : PatternFilterableResourceTransformer(patternSet) { - private val includeResources by unsafeLazy { - @Suppress("DEPRECATION") - include(resources.get()) - } + private val includeResources by unsafeLazy { @Suppress("DEPRECATION") include(resources.get()) } - @get:Internal - protected val found: MutableSet = mutableSetOf() + @get:Internal protected val found: MutableSet = mutableSetOf() @get:Deprecated("Use `include(..)` instead") @get:Input public open val resources: SetProperty = objectFactory.setProperty() - @Inject - public constructor(objectFactory: ObjectFactory) : this(objectFactory, PatternSet()) + @Inject public constructor(objectFactory: ObjectFactory) : this(objectFactory, PatternSet()) override fun canTransformResource(element: FileTreeElement): Boolean { // Init once before patternSpec is accessed. diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt index 92aeadc21..9b1885a5c 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformer.kt @@ -21,16 +21,15 @@ import org.gradle.api.tasks.Internal /** * Resources transformer that merges Properties files. * - * The default merge strategy discards duplicate values coming from additional - * resources. This behavior can be changed by setting a value for the [mergeStrategy] property, - * such as [MergeStrategy.First] (default), [MergeStrategy.Latest] or [MergeStrategy.Append]. If the merge strategy is - * [MergeStrategy.Latest] then the last value of a matching property entry will be used. If the - * merge strategy is [MergeStrategy.Append] then the property values will be combined, using a - * merge separator (default value is ','). The merge separator can be changed by - * setting a value for the [mergeSeparator] property. + * The default merge strategy discards duplicate values coming from additional resources. This + * behavior can be changed by setting a value for the [mergeStrategy] property, such as + * [MergeStrategy.First] (default), [MergeStrategy.Latest] or [MergeStrategy.Append]. If the merge + * strategy is [MergeStrategy.Latest] then the last value of a matching property entry will be used. + * If the merge strategy is [MergeStrategy.Append] then the property values will be combined, using + * a merge separator (default value is ','). The merge separator can be changed by setting a value + * for the [mergeSeparator] property. * - * Say there are two properties files A and B with the - * following entries: + * Say there are two properties files A and B with the following entries: * * **A** * - key1 = value1 @@ -61,22 +60,20 @@ import org.gradle.api.tasks.Internal * - key2 = value2;balue2 * - key3 = value3 * - * With `mergeStrategy = MergeStrategy.Fail` the transformation will fail if there are conflicting values. + * With `mergeStrategy = MergeStrategy.Fail` the transformation will fail if there are conflicting + * values. * - * There are three additional properties that can be set: [paths], [mappings], - * and [keyTransformer]. - * The first contains a list of strings or regexes that will be used to determine if - * a path should be transformed or not. The merge strategy and merge separator are - * taken from the global settings. + * There are three additional properties that can be set: [paths], [mappings], and [keyTransformer]. + * The first contains a list of strings or regexes that will be used to determine if a path should + * be transformed or not. The merge strategy and merge separator are taken from the global settings. * - * The [mappings] property allows you to define merge strategy and separator per - * path. If either [paths] or [mappings] is defined then no other path - * entries will be merged. [mappings] has precedence over [paths] if both - * are defined. + * The [mappings] property allows you to define merge strategy and separator per path. If either + * [paths] or [mappings] is defined then no other path entries will be merged. [mappings] has + * precedence over [paths] if both are defined. * - * If you need to transform keys in properties files, e.g. because they contain class - * names about to be relocated, you can set the [keyTransformer] property to a - * closure that receives the original key and returns the key name to be used. + * If you need to transform keys in properties files, e.g. because they contain class names about to + * be relocated, you can set the [keyTransformer] property to a closure that receives the original + * key and returns the key name to be used. * * Example: * ```groovy @@ -93,43 +90,40 @@ import org.gradle.api.tasks.Internal * } * ``` * - * Related to [org.apache.maven.plugins.shade.resource.properties.PropertiesTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/properties/PropertiesTransformer.java). + * Related to + * [org.apache.maven.plugins.shade.resource.properties.PropertiesTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/properties/PropertiesTransformer.java). * * @author Andres Almiray * @author Marc Philipp */ @CacheableTransformer -public open class PropertiesFileTransformer @Inject constructor( - final override val objectFactory: ObjectFactory, -) : ResourceTransformer { - private inline val charset get() = Charset.forName(charsetName.get()) +public open class PropertiesFileTransformer +@Inject +constructor(final override val objectFactory: ObjectFactory) : ResourceTransformer { + private inline val charset + get() = Charset.forName(charsetName.get()) - @get:Internal - internal val conflicts: MutableMap> = mutableMapOf() + @get:Internal internal val conflicts: MutableMap> = mutableMapOf() - @get:Internal - internal val propertiesEntries = mutableMapOf() + @get:Internal internal val propertiesEntries = mutableMapOf() - @get:Input - public open val paths: SetProperty = objectFactory.setProperty() + @get:Input public open val paths: SetProperty = objectFactory.setProperty() @get:Input public open val mappings: MapProperty> = objectFactory.mapProperty() @get:Input - public open val mergeStrategy: Property = objectFactory.property(MergeStrategy.First) + public open val mergeStrategy: Property = + objectFactory.property(MergeStrategy.First) - @get:Input - public open val mergeSeparator: Property = objectFactory.property(",") + @get:Input public open val mergeSeparator: Property = objectFactory.property(",") - /** - * The character set to use when reading and writing property files. - * Defaults to `ISO-8859-1`. - */ + /** The character set to use when reading and writing property files. Defaults to `ISO-8859-1`. */ @get:Input public open val charsetName: Property = objectFactory.property(Charsets.ISO_8859_1.name()) - @get:Internal // TODO: should be @Input, but it can't be serialized, see https://github.com/GradleUp/shadow/pull/1208. + @get:Internal // TODO: should be @Input, but it can't be serialized, see + // https://github.com/GradleUp/shadow/pull/1208. public open var keyTransformer: (String) -> String = IDENTITY override fun canTransformResource(element: FileTreeElement): Boolean { @@ -171,7 +165,10 @@ public open class PropertiesFileTransformer @Inject constructor( } } - private fun loadAndTransformKeys(inputStream: InputStream, action: (key: String, value: String) -> Unit) { + private fun loadAndTransformKeys( + inputStream: InputStream, + action: (key: String, value: String) -> Unit, + ) { val props = Properties().apply { load(inputStream.bufferedReader(charset)) } props.forEach { action(keyTransformer(it.key as String), it.value as String) } } @@ -210,11 +207,18 @@ public open class PropertiesFileTransformer @Inject constructor( override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) { if (conflicts.isNotEmpty()) { - val message = "The following properties files have conflicting property values and cannot be merged:" + - conflicts.map { (path, props) -> - path + props.map { "Property ${it.key} is duplicated ${it.value} times with different values" } - .joinToString(separator = "\n * ", prefix = "\n * ") - }.joinToString(separator = "\n * ", prefix = "\n * ") + val message = + "The following properties files have conflicting property values and cannot be merged:" + + conflicts + .map { (path, props) -> + path + + props + .map { + "Property ${it.key} is duplicated ${it.value} times with different values" + } + .joinToString(separator = "\n * ", prefix = "\n * ") + } + .joinToString(separator = "\n * ", prefix = "\n * ") error(message) } @@ -229,8 +233,7 @@ public open class PropertiesFileTransformer @Inject constructor( First, Latest, Append, - Fail, - ; + Fail; public companion object { @JvmStatic diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt index bb3d523ac..53e72a671 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ResourceTransformer.kt @@ -9,7 +9,8 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.tasks.Internal /** - * Modified from [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ResourceTransformer.java). * * @author Jason van Zyl * @author Charlie Knudsen @@ -18,37 +19,38 @@ import org.gradle.api.tasks.Internal public interface ResourceTransformer : Named { public fun canTransformResource(element: FileTreeElement): Boolean - @Throws(IOException::class) - public fun transform(context: TransformerContext) + @Throws(IOException::class) public fun transform(context: TransformerContext) public fun hasTransformedResource(): Boolean @Throws(IOException::class) public fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean) - @Suppress("unused") // Used by Gradle side, see https://github.com/GradleUp/shadow/pull/1289#issuecomment-2915738983. + @Suppress("unused") // Used by Gradle side, see + // https://github.com/GradleUp/shadow/pull/1289#issuecomment-2915738983. @Internal override fun getName(): String = this::class.java.simpleName /** - * This is used for creating Gradle's lazy properties in the subclass, Shadow's build-in transformers that depend on - * this have been injected via [ObjectFactory.newInstance]. Custom transformers should implement or inject - * this property if they need to access it. + * This is used for creating Gradle's lazy properties in the subclass, Shadow's build-in + * transformers that depend on this have been injected via [ObjectFactory.newInstance]. Custom + * transformers should implement or inject this property if they need to access it. */ @get:Internal public val objectFactory: ObjectFactory - get() = throw NotImplementedError("You have to make sure this has been implemented or injected.") + get() = + throw NotImplementedError("You have to make sure this has been implemented or injected.") /** - * This also implements [ResourceTransformer] but no-op, which means it could be used by Kotlin delegations. + * This also implements [ResourceTransformer] but no-op, which means it could be used by Kotlin + * delegations. */ public companion object : ResourceTransformer { @JvmStatic public fun Class.create(objectFactory: ObjectFactory): T { // If the constructor takes a single ObjectFactory, inject it in. - val constructor = constructors.find { - it.parameterTypes.singleOrNull() == ObjectFactory::class.java - } + val constructor = + constructors.find { it.parameterTypes.singleOrNull() == ObjectFactory::class.java } return if (constructor != null) { objectFactory.newInstance(this@create) } else { @@ -57,8 +59,14 @@ public interface ResourceTransformer : Named { } public override fun canTransformResource(element: FileTreeElement): Boolean = false + public override fun transform(context: TransformerContext): Unit = Unit - public override fun modifyOutputStream(os: ZipOutputStream, preserveFileTimestamps: Boolean): Unit = Unit + + public override fun modifyOutputStream( + os: ZipOutputStream, + preserveFileTimestamps: Boolean, + ): Unit = Unit + public override fun hasTransformedResource(): Boolean = false } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt index 616603e2b..bcb95ab21 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformer.kt @@ -8,26 +8,28 @@ import org.gradle.api.tasks.Internal import org.gradle.api.tasks.util.PatternSet /** - * Resources transformer that appends entries in `META-INF/services` resources into - * a single resource. For example, if there are several `META-INF/services/org.apache.maven.project.ProjectBuilder` - * resources spread across many JARs the individual entries will all be concatenated into a single - * `META-INF/services/org.apache.maven.project.ProjectBuilder` resource packaged into the resultant JAR produced - * by the shading process. + * Resources transformer that appends entries in `META-INF/services` resources into a single + * resource. For example, if there are several + * `META-INF/services/org.apache.maven.project.ProjectBuilder` resources spread across many JARs the + * individual entries will all be concatenated into a single + * `META-INF/services/org.apache.maven.project.ProjectBuilder` resource packaged into the resultant + * JAR produced by the shading process. * - * Modified from [org.apache.maven.plugins.shade.resource.ServicesResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ServicesResourceTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/ServicesResourceTransformer.java). * * @author jvanzyl * @author Charlie Knudsen * @author John Engelman */ @CacheableTransformer -public open class ServiceFileTransformer @JvmOverloads constructor( - patternSet: PatternSet = PatternSet() - .include(SERVICES_PATTERN) - .exclude(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR), +public open class ServiceFileTransformer +@JvmOverloads +constructor( + patternSet: PatternSet = + PatternSet().include(SERVICES_PATTERN).exclude(PATH_LEGACY_GROOVY_EXTENSION_MODULE_DESCRIPTOR) ) : PatternFilterableResourceTransformer(patternSet = patternSet) { - @get:Internal - internal val serviceEntries = mutableMapOf>() + @get:Internal internal val serviceEntries = mutableMapOf>() @get:Internal // No need to mark this as an input as `getIncludes` is already marked as `@Input`. public open var path: String = SERVICES_PATH @@ -38,12 +40,13 @@ public open class ServiceFileTransformer @JvmOverloads constructor( } override fun transform(context: TransformerContext) { - val resource = path + "/" + - context.relocators.relocateClass(context.path.substringAfter("$path/")) + val resource = + path + "/" + context.relocators.relocateClass(context.path.substringAfter("$path/")) val out = serviceEntries.getOrPut(resource) { mutableSetOf() } - context.inputStream.bufferedReader().use { it.readLines() }.forEach { line -> - out.add(context.relocators.relocateClass(line)) - } + context.inputStream + .bufferedReader() + .use { it.readLines() } + .forEach { line -> out.add(context.relocators.relocateClass(line)) } } override fun hasTransformedResource(): Boolean = serviceEntries.isNotEmpty() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt index 71e3f5cfa..ad91106c1 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/TransformerContext.kt @@ -3,7 +3,9 @@ package com.github.jengelman.gradle.plugins.shadow.transformers import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import java.io.InputStream -public data class TransformerContext @JvmOverloads constructor( +public data class TransformerContext +@JvmOverloads +constructor( val path: String, val inputStream: InputStream, val relocators: Set = emptySet(), @@ -14,17 +16,24 @@ public data class TransformerContext @JvmOverloads constructor( private var relocators = emptySet() public fun path(path: String): Builder = apply { this.path = path } - public fun inputStream(inputStream: InputStream): Builder = apply { this.inputStream = inputStream } - public fun relocators(relocators: Set): Builder = apply { this.relocators = relocators } - public fun build(): TransformerContext = TransformerContext( - path = path, - inputStream = requireNotNull(inputStream) { "inputStream is required" }, - relocators = relocators, - ) + + public fun inputStream(inputStream: InputStream): Builder = apply { + this.inputStream = inputStream + } + + public fun relocators(relocators: Set): Builder = apply { + this.relocators = relocators + } + + public fun build(): TransformerContext = + TransformerContext( + path = path, + inputStream = requireNotNull(inputStream) { "inputStream is required" }, + relocators = relocators, + ) } public companion object { - @JvmStatic - public fun builder(): Builder = Builder() + @JvmStatic public fun builder(): Builder = Builder() } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt index 2e02fd805..990bb90f2 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/XmlAppendingTransformer.kt @@ -22,37 +22,39 @@ import org.xml.sax.InputSource /** * Appends multiple occurrences of some XML file. * - * Modified from [org.apache.maven.plugins.shade.resource.XmlAppendingTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/XmlAppendingTransformer.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.XmlAppendingTransformer.java](https://github.com/apache/maven-shade-plugin/blob/master/src/main/java/org/apache/maven/plugins/shade/resource/XmlAppendingTransformer.java). * * @author John Engelman */ @CacheableTransformer -public open class XmlAppendingTransformer @Inject constructor( - final override val objectFactory: ObjectFactory, -) : ResourceTransformer { +public open class XmlAppendingTransformer +@Inject +constructor(final override val objectFactory: ObjectFactory) : ResourceTransformer { private var doc: Document? = null - @get:Input - public open val ignoreDtd: Property = objectFactory.property(true) + @get:Input public open val ignoreDtd: Property = objectFactory.property(true) - @get:Input - public open val resource: Property = objectFactory.property("") + @get:Input public open val resource: Property = objectFactory.property("") override fun canTransformResource(element: FileTreeElement): Boolean { return resource.get().equals(element.path, ignoreCase = true) } override fun transform(context: TransformerContext) { - val r = try { - SAXBuilder(XMLReaders.NONVALIDATING).apply { - expandEntities = false - if (ignoreDtd.get()) { - entityResolver = EntityResolver { _, _ -> InputSource(StringReader("")) } - } - }.build(context.inputStream) - } catch (e: JDOMException) { - throw IOException("Error processing resource ${resource.get()}: ${e.message}", e) - } + val r = + try { + SAXBuilder(XMLReaders.NONVALIDATING) + .apply { + expandEntities = false + if (ignoreDtd.get()) { + entityResolver = EntityResolver { _, _ -> InputSource(StringReader("")) } + } + } + .build(context.inputStream) + } catch (e: JDOMException) { + throw IOException("Error processing resource ${resource.get()}: ${e.message}", e) + } if (doc == null) { doc = r @@ -65,9 +67,7 @@ public open class XmlAppendingTransformer @Inject constructor( mergedEl.setAttribute(a) } } - root.children.forEach { n -> - doc!!.rootElement.addContent(n.clone()) - } + root.children.forEach { n -> doc!!.rootElement.addContent(n.clone()) } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index 2fdaadf7e..803ca4ba4 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -48,212 +48,225 @@ class ShadowPropertiesTest { @BeforeEach fun beforeEach() { - project = ProjectBuilder.builder().withName(PROJECT_NAME).build().also { - it.version = VERSION - it.plugins.apply(ShadowPlugin::class.java) - } + project = + ProjectBuilder.builder().withName(PROJECT_NAME).build().also { + it.version = VERSION + it.plugins.apply(ShadowPlugin::class.java) + } } @Test - fun misc() = with(project) { - assertThat(plugins.hasPlugin(ShadowPlugin::class.java)).isTrue() - assertThat(plugins.hasPlugin(LegacyShadowPlugin::class.java)).isTrue() - assertThat(tasks.findByName(SHADOW_JAR_TASK_NAME)).isNull() + fun misc() = + with(project) { + assertThat(plugins.hasPlugin(ShadowPlugin::class.java)).isTrue() + assertThat(plugins.hasPlugin(LegacyShadowPlugin::class.java)).isTrue() + assertThat(tasks.findByName(SHADOW_JAR_TASK_NAME)).isNull() - with(extensions.getByType(ShadowExtension::class.java)) { - assertThat(addShadowVariantIntoJavaComponent.get()).isTrue() - assertThat(addTargetJvmVersionAttribute.get()).isTrue() - assertThat(bundlingAttribute.get()).isEqualTo(Bundling.SHADOWED) + with(extensions.getByType(ShadowExtension::class.java)) { + assertThat(addShadowVariantIntoJavaComponent.get()).isTrue() + assertThat(addTargetJvmVersionAttribute.get()).isTrue() + assertThat(bundlingAttribute.get()).isEqualTo(Bundling.SHADOWED) + } } - } @Test - fun inheritManifestAttrsFromJars() = with(project) { - plugins.apply(JavaPlugin::class.java) - tasks.jar.configure { - it.manifest.attributes["jar"] = "fromJar" - } - val jar1 = tasks.register("jar1", Jar::class.java) { - it.manifest.attributes["jar1"] = "fromJar1" - } - val jar2 = tasks.register("jar2", Jar::class.java) { - it.manifest.attributes["jar2"] = "fromJar2" - } - tasks.shadowJar.configure { - it.manifest.attributes["shadowJar"] = "fromShadowJar" - it.manifest.from(jar1.get().manifest) - @Suppress("DEPRECATION") // TODO: remove this once InheritManifest is removed. - it.manifest.inheritFrom(jar2.get().manifest) + fun inheritManifestAttrsFromJars() = + with(project) { + plugins.apply(JavaPlugin::class.java) + tasks.jar.configure { it.manifest.attributes["jar"] = "fromJar" } + val jar1 = + tasks.register("jar1", Jar::class.java) { it.manifest.attributes["jar1"] = "fromJar1" } + val jar2 = + tasks.register("jar2", Jar::class.java) { it.manifest.attributes["jar2"] = "fromJar2" } + tasks.shadowJar.configure { + it.manifest.attributes["shadowJar"] = "fromShadowJar" + it.manifest.from(jar1.get().manifest) + @Suppress("DEPRECATION") // TODO: remove this once InheritManifest is removed. + it.manifest.inheritFrom(jar2.get().manifest) + } + // Call effectiveManifest as a way to force merging to happen like writing the jar would. + assertThat(tasks.shadowJar.get().manifest.effectiveManifest.attributes) + .containsOnly( + "Manifest-Version" to "1.0", + "jar" to "fromJar", + "jar1" to "fromJar1", + "jar2" to "fromJar2", + "shadowJar" to "fromShadowJar", + ) } - // Call effectiveManifest as a way to force merging to happen like writing the jar would. - assertThat(tasks.shadowJar.get().manifest.effectiveManifest.attributes).containsOnly( - "Manifest-Version" to "1.0", - "jar" to "fromJar", - "jar1" to "fromJar1", - "jar2" to "fromJar2", - "shadowJar" to "fromShadowJar", - ) - } @Test - fun inheritManifestMainClassFromJar() = with(project) { - plugins.apply(JavaPlugin::class.java) - tasks.jar.configure { - it.manifest.attributes[mainClassAttributeKey] = "Main" - } - tasks.shadowJar.configure { - it.mainClass.set("Main2") // This should not override the inherited one from jar. + fun inheritManifestMainClassFromJar() = + with(project) { + plugins.apply(JavaPlugin::class.java) + tasks.jar.configure { it.manifest.attributes[mainClassAttributeKey] = "Main" } + tasks.shadowJar.configure { + it.mainClass.set("Main2") // This should not override the inherited one from jar. + } + assertThat(tasks.shadowJar.get().manifest.attributes) + .containsOnly("Manifest-Version" to "1.0", mainClassAttributeKey to "Main") } - assertThat(tasks.shadowJar.get().manifest.attributes).containsOnly( - "Manifest-Version" to "1.0", - mainClassAttributeKey to "Main", - ) - } @Test - fun applyJavaPlugin() = with(project) { - plugins.apply(JavaPlugin::class.java) - val shadowJarTask = tasks.shadowJar.get() - val shadowConfig = configurations.shadow.get() - val assembleTask = tasks.getByName(ASSEMBLE_TASK_NAME) + fun applyJavaPlugin() = + with(project) { + plugins.apply(JavaPlugin::class.java) + val shadowJarTask = tasks.shadowJar.get() + val shadowConfig = configurations.shadow.get() + val assembleTask = tasks.getByName(ASSEMBLE_TASK_NAME) - assertThat(shadowConfig.artifacts.files).containsOnly(shadowJarTask.archiveFile.get().asFile) - assertThat(assembleTask.dependsOnTaskNames).containsOnly(shadowJarTask.name) + assertThat(shadowConfig.artifacts.files).containsOnly(shadowJarTask.archiveFile.get().asFile) + assertThat(assembleTask.dependsOnTaskNames).containsOnly(shadowJarTask.name) - // Check inherited properties. - with(shadowJarTask as Jar) { - assertThat(group).isEqualTo(LifecycleBasePlugin.BUILD_GROUP) - assertThat(description).isEqualTo("Create a combined JAR of project and runtime dependencies") + // Check inherited properties. + with(shadowJarTask as Jar) { + assertThat(group).isEqualTo(LifecycleBasePlugin.BUILD_GROUP) + assertThat(description) + .isEqualTo("Create a combined JAR of project and runtime dependencies") - assertThat(archiveAppendix.orNull).isNull() - assertThat(archiveBaseName.get()).isEqualTo(PROJECT_NAME) - assertThat(archiveClassifier.get()).isEqualTo("all") - assertThat(archiveExtension.get()).isEqualTo("jar") - assertThat(archiveFileName.get()).isEqualTo("my-project-1.0.0-all.jar") - assertThat(archiveVersion.get()).isEqualTo(version) - assertThat(archiveFile.get().asFile).all { - isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) - isEqualTo(projectDir.resolve("build/libs/my-project-1.0.0-all.jar")) - } - assertThat(destinationDirectory.get().asFile).all { - isEqualTo(layout.buildDirectory.dir("libs").get().asFile) - isEqualTo(projectDir.resolve("build/libs")) - } + assertThat(archiveAppendix.orNull).isNull() + assertThat(archiveBaseName.get()).isEqualTo(PROJECT_NAME) + assertThat(archiveClassifier.get()).isEqualTo("all") + assertThat(archiveExtension.get()).isEqualTo("jar") + assertThat(archiveFileName.get()).isEqualTo("my-project-1.0.0-all.jar") + assertThat(archiveVersion.get()).isEqualTo(version) + assertThat(archiveFile.get().asFile).all { + isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) + isEqualTo(projectDir.resolve("build/libs/my-project-1.0.0-all.jar")) + } + assertThat(destinationDirectory.get().asFile).all { + isEqualTo(layout.buildDirectory.dir("libs").get().asFile) + isEqualTo(projectDir.resolve("build/libs")) + } - assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.EXCLUDE) - } + assertThat(duplicatesStrategy).isEqualTo(DuplicatesStrategy.EXCLUDE) + } - // Check self properties. - with(shadowJarTask) { - assertThat(addMultiReleaseAttribute.get()).isTrue() - assertThat(enableAutoRelocation.get()).isFalse() - assertThat(enableKotlinModuleRemapping.get()).isTrue() - assertThat(failOnDuplicateEntries.get()).isFalse() - assertThat(minimizeJar.get()).isFalse() - assertThat(mainClass.orNull).isNull() + // Check self properties. + with(shadowJarTask) { + assertThat(addMultiReleaseAttribute.get()).isTrue() + assertThat(enableAutoRelocation.get()).isFalse() + assertThat(enableKotlinModuleRemapping.get()).isTrue() + assertThat(failOnDuplicateEntries.get()).isFalse() + assertThat(minimizeJar.get()).isFalse() + assertThat(mainClass.orNull).isNull() - assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) - assertThat(configurations.get()).containsOnly(runtimeConfiguration) + assertThat(relocationPrefix.get()).isEqualTo(ShadowBasePlugin.SHADOW) + assertThat(configurations.get()).containsOnly(runtimeConfiguration) + } } - } @Test - fun applyApplicationPlugin() = with(project) { - plugins.apply(ApplicationPlugin::class.java) - val shadowJarTask = tasks.shadowJar.get() - val runShadowTask = tasks.runShadow.get() - val startShadowScripts = tasks.startShadowScripts.get() - val installShadowDist = tasks.installShadowDist.get() - val shadowDistZip = tasks.shadowDistZip.get() - val shadowDistTar = tasks.shadowDistTar.get() + fun applyApplicationPlugin() = + with(project) { + plugins.apply(ApplicationPlugin::class.java) + val shadowJarTask = tasks.shadowJar.get() + val runShadowTask = tasks.runShadow.get() + val startShadowScripts = tasks.startShadowScripts.get() + val installShadowDist = tasks.installShadowDist.get() + val shadowDistZip = tasks.shadowDistZip.get() + val shadowDistTar = tasks.shadowDistTar.get() - with(runShadowTask) { - assertThat(description).isEqualTo("Runs this project as a JVM application using the shadow jar") - assertThat(group).isEqualTo(ApplicationPlugin.APPLICATION_GROUP) - assertThat(classpath.files).containsOnly(shadowJarTask.archiveFile.get().asFile) - assertThat(mainModule.orNull).isEqualTo(applicationExtension.mainModule.orNull) - assertThat(mainClass.orNull).isEqualTo(applicationExtension.mainClass.orNull) - assertThat(jvmArguments.get()).isEqualTo(applicationExtension.applicationDefaultJvmArgs) - assertThat(modularity.inferModulePath.orNull) - .isEqualTo(javaPluginExtension.modularity.inferModulePath.orNull) - assertThat(javaLauncher.get().metadata.jvmVersion) - .isEqualTo(javaToolchainService.launcherFor(javaPluginExtension.toolchain).get().metadata.jvmVersion) - } - - with(startShadowScripts) { - assertThat(description).isEqualTo("Creates OS specific scripts to run the project as a JVM application using the shadow jar") - assertThat(classpath?.files).isNotNull().containsOnly(shadowJarTask.archiveFile.get().asFile) - assertThat(mainModule.orNull).isEqualTo(applicationExtension.mainModule.orNull) - assertThat(mainClass.orNull).isEqualTo(applicationExtension.mainClass.orNull) - assertThat(applicationName).isEqualTo(applicationExtension.applicationName) - assertThat(outputDir).isNotNull().all { - isEqualTo(layout.buildDirectory.dir("scriptsShadow").get().asFile) - isEqualTo(projectDir.resolve("build/scriptsShadow")) + with(runShadowTask) { + assertThat(description) + .isEqualTo("Runs this project as a JVM application using the shadow jar") + assertThat(group).isEqualTo(ApplicationPlugin.APPLICATION_GROUP) + assertThat(classpath.files).containsOnly(shadowJarTask.archiveFile.get().asFile) + assertThat(mainModule.orNull).isEqualTo(applicationExtension.mainModule.orNull) + assertThat(mainClass.orNull).isEqualTo(applicationExtension.mainClass.orNull) + assertThat(jvmArguments.get()).isEqualTo(applicationExtension.applicationDefaultJvmArgs) + assertThat(modularity.inferModulePath.orNull) + .isEqualTo(javaPluginExtension.modularity.inferModulePath.orNull) + assertThat(javaLauncher.get().metadata.jvmVersion) + .isEqualTo( + javaToolchainService + .launcherFor(javaPluginExtension.toolchain) + .get() + .metadata + .jvmVersion + ) } - assertThat(executableDir).isEqualTo(applicationExtension.executableDir) - assertThat(defaultJvmOpts).isEqualTo(applicationExtension.applicationDefaultJvmArgs) - assertThat(modularity.inferModulePath.orNull) - .isEqualTo(javaPluginExtension.modularity.inferModulePath.orNull) - } - with(installShadowDist) { - assertThat(description).isEqualTo("Installs the project as a distribution as-is.") - assertThat(group).isEqualTo("distribution") - assertThat(destinationDir).isNotNull() - .isEqualTo(projectDir.resolve("build/install/my-project-shadow")) - } + with(startShadowScripts) { + assertThat(description) + .isEqualTo( + "Creates OS specific scripts to run the project as a JVM application using the shadow jar" + ) + assertThat(classpath?.files) + .isNotNull() + .containsOnly(shadowJarTask.archiveFile.get().asFile) + assertThat(mainModule.orNull).isEqualTo(applicationExtension.mainModule.orNull) + assertThat(mainClass.orNull).isEqualTo(applicationExtension.mainClass.orNull) + assertThat(applicationName).isEqualTo(applicationExtension.applicationName) + assertThat(outputDir).isNotNull().all { + isEqualTo(layout.buildDirectory.dir("scriptsShadow").get().asFile) + isEqualTo(projectDir.resolve("build/scriptsShadow")) + } + assertThat(executableDir).isEqualTo(applicationExtension.executableDir) + assertThat(defaultJvmOpts).isEqualTo(applicationExtension.applicationDefaultJvmArgs) + assertThat(modularity.inferModulePath.orNull) + .isEqualTo(javaPluginExtension.modularity.inferModulePath.orNull) + } - listOf( - shadowDistZip, - shadowDistTar, - ).forEach { - with(it as AbstractArchiveTask) { - assertThat(description).isEqualTo("Bundles the project as a distribution.") + with(installShadowDist) { + assertThat(description).isEqualTo("Installs the project as a distribution as-is.") assertThat(group).isEqualTo("distribution") - assertThat(archiveAppendix.orNull).isNull() - assertThat(archiveBaseName.get()).isEqualTo("my-project-shadow") - assertThat(archiveClassifier.orNull).isNull() - assertThat(archiveVersion.get()).isEqualTo(version) - assertThat(destinationDirectory.get().asFile).all { - isEqualTo(layout.buildDirectory.dir("distributions").get().asFile) - isEqualTo(projectDir.resolve("build/distributions")) + assertThat(destinationDir) + .isNotNull() + .isEqualTo(projectDir.resolve("build/install/my-project-shadow")) + } + + listOf(shadowDistZip, shadowDistTar).forEach { + with(it as AbstractArchiveTask) { + assertThat(description).isEqualTo("Bundles the project as a distribution.") + assertThat(group).isEqualTo("distribution") + assertThat(archiveAppendix.orNull).isNull() + assertThat(archiveBaseName.get()).isEqualTo("my-project-shadow") + assertThat(archiveClassifier.orNull).isNull() + assertThat(archiveVersion.get()).isEqualTo(version) + assertThat(destinationDirectory.get().asFile).all { + isEqualTo(layout.buildDirectory.dir("distributions").get().asFile) + isEqualTo(projectDir.resolve("build/distributions")) + } } } - } - with(shadowDistZip) { - assertThat(archiveExtension.get()).isEqualTo("zip") - assertThat(archiveFileName.get()).isEqualTo("my-project-shadow-1.0.0.zip") - assertThat(archiveFile.get().asFile).all { - isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) - isEqualTo(projectDir.resolve("build/distributions/my-project-shadow-1.0.0.zip")) + with(shadowDistZip) { + assertThat(archiveExtension.get()).isEqualTo("zip") + assertThat(archiveFileName.get()).isEqualTo("my-project-shadow-1.0.0.zip") + assertThat(archiveFile.get().asFile).all { + isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) + isEqualTo(projectDir.resolve("build/distributions/my-project-shadow-1.0.0.zip")) + } } - } - with(shadowDistTar) { - assertThat(archiveExtension.get()).isEqualTo("tar") - assertThat(archiveFileName.get()).isEqualTo("my-project-shadow-1.0.0.tar") - assertThat(archiveFile.get().asFile).all { - isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) - isEqualTo(projectDir.resolve("build/distributions/my-project-shadow-1.0.0.tar")) + with(shadowDistTar) { + assertThat(archiveExtension.get()).isEqualTo("tar") + assertThat(archiveFileName.get()).isEqualTo("my-project-shadow-1.0.0.tar") + assertThat(archiveFile.get().asFile).all { + isEqualTo(destinationDirectory.file(archiveFileName).get().asFile) + isEqualTo(projectDir.resolve("build/distributions/my-project-shadow-1.0.0.tar")) + } } } - } @Test - fun applyJavaGradlePlugin() = with(project) { - plugins.apply(JavaGradlePluginPlugin::class.java) - val api = configurations.named(API_CONFIGURATION_NAME).get() - val compileOnly = configurations.named(COMPILE_ONLY_CONFIGURATION_NAME).get() - val gradleApi = dependencies.gradleApi() - assertThat(api.dependencies).containsNone(gradleApi) - assertThat(compileOnly.dependencies).containsOnly(gradleApi) - } + fun applyJavaGradlePlugin() = + with(project) { + plugins.apply(JavaGradlePluginPlugin::class.java) + val api = configurations.named(API_CONFIGURATION_NAME).get() + val compileOnly = configurations.named(COMPILE_ONLY_CONFIGURATION_NAME).get() + val gradleApi = dependencies.gradleApi() + assertThat(api.dependencies).containsNone(gradleApi) + assertThat(compileOnly.dependencies).containsOnly(gradleApi) + } private companion object { const val PROJECT_NAME = "my-project" const val VERSION = "1.0.0" - val Task.dependsOnTaskNames: List get() = dependsOn.filterIsInstance().map(Named::getName) + val Task.dependsOnTaskNames: List + get() = dependsOn.filterIsInstance().map(Named::getName) - val TaskContainer.jar: TaskProvider get() = named("jar", Jar::class.java) + val TaskContainer.jar: TaskProvider + get() = named("jar", Jar::class.java) } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproduciblePropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproduciblePropertiesTest.kt index e0775596a..42b6b08eb 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproduciblePropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/ReproduciblePropertiesTest.kt @@ -21,69 +21,73 @@ class ReproduciblePropertiesTest { @ParameterizedTest @MethodSource("generalCharsetsProvider") fun asciiProps(charset: Charset) { - val output = ReproducibleProperties().also { props -> - props["key"] = "value" - props["key2"] = "value2" - props["a"] = "b" - props["d"] = "e" - props["0"] = "1" - props["b"] = "c" - props["c"] = "d" - props["e"] = "f" - }.writeToString(charset) + val output = + ReproducibleProperties() + .also { props -> + props["key"] = "value" + props["key2"] = "value2" + props["a"] = "b" + props["d"] = "e" + props["0"] = "1" + props["b"] = "c" + props["c"] = "d" + props["e"] = "f" + } + .writeToString(charset) - assertThat(output).isEqualTo( - """ - |0=1 - |a=b - |b=c - |c=d - |d=e - |e=f - |key=value - |key2=value2 - | - """.trimMargin(), - ) + assertThat(output) + .isEqualTo( + """ + |0=1 + |a=b + |b=c + |c=d + |d=e + |e=f + |key=value + |key2=value2 + |""" + .trimMargin() + ) } @ParameterizedTest @MethodSource("utfCharsetsProvider") fun utfProps(charset: Charset) { - val output = ReproducibleProperties().also { props -> - props["äöüß"] = "aouss" - props["áèô"] = "aeo" - props["€²³"] = "x" - props["传傳磨宿说説"] = "b" - }.writeToString(charset) + val output = + ReproducibleProperties() + .also { props -> + props["äöüß"] = "aouss" + props["áèô"] = "aeo" + props["€²³"] = "x" + props["传傳磨宿说説"] = "b" + } + .writeToString(charset) - assertThat(output).isEqualTo( - """ - |áèô=aeo - |äöüß=aouss - |€²³=x - |传傳磨宿说説=b - | - """.trimMargin(), - ) + assertThat(output) + .isEqualTo( + """ + |áèô=aeo + |äöüß=aouss + |€²³=x + |传傳磨宿说説=b + |""" + .trimMargin() + ) } private companion object Companion { @JvmStatic - fun generalCharsetsProvider() = listOf( - StandardCharsets.ISO_8859_1, - StandardCharsets.US_ASCII, - ) + utfCharsetsProvider() + fun generalCharsetsProvider() = + listOf(StandardCharsets.ISO_8859_1, StandardCharsets.US_ASCII) + utfCharsetsProvider() - @JvmStatic - fun utfCharsetsProvider() = listOf( - StandardCharsets.UTF_8, - StandardCharsets.UTF_16, - ) + @JvmStatic fun utfCharsetsProvider() = listOf(StandardCharsets.UTF_8, StandardCharsets.UTF_16) fun ReproducibleProperties.writeToString(charset: Charset): String { - return ByteArrayOutputStream().also { writeWithoutComments(charset, it) } - .toString(charset.name()).invariantEolString + return ByteArrayOutputStream() + .also { writeWithoutComments(charset, it) } + .toString(charset.name()) + .invariantEolString } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt index 6ce670cce..5cc83d4ea 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/RelocatorRemapperTest.kt @@ -11,40 +11,51 @@ class RelocatorRemapperTest { @ParameterizedTest @MethodSource("signaturePatternsProvider") fun relocateSignaturePatterns(input: String, expected: String) { - val relocator = RelocatorRemapper( - relocators = setOf( - SimpleRelocator("org.package", "shadow.org.package"), - ), - ) + val relocator = + RelocatorRemapper(relocators = setOf(SimpleRelocator("org.package", "shadow.org.package"))) assertThat(relocator.map(input)).isEqualTo(expected) } private companion object { val primitiveTypes = setOf('B', 'C', 'D', 'F', 'I', 'J', 'S', 'Z') - val primitiveTypePatterns = primitiveTypes.map { - // Methods like `void method(boolean arg1, org.package.ClassA arg2)` - Arguments.of("(${it}Lorg/package/ClassA;)V", "(${it}Lshadow/org/package/ClassA;)V") - } + val primitiveTypePatterns = + primitiveTypes.map { + // Methods like `void method(boolean arg1, org.package.ClassA arg2)` + Arguments.of("(${it}Lorg/package/ClassA;)V", "(${it}Lshadow/org/package/ClassA;)V") + } @JvmStatic - fun signaturePatternsProvider() = listOf( - // Normal class: `org.package.ClassA` - Arguments.of("Lorg/package/ClassA;", "Lshadow/org/package/ClassA;"), - // Array class: `org.package.ClassA[]` - Arguments.of("[Lorg/package/ClassA;", "[Lshadow/org/package/ClassA;"), - // Multidimensional array of class: `org.package.ClassA[][]` - Arguments.of("[[Lorg/package/ClassA;", "[[Lshadow/org/package/ClassA;"), - // Multiple classes: `org.package.ClassA org.package.ClassB` - Arguments.of("Lorg/package/ClassA;Lorg/package/ClassB;", "Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;"), - // Multiple classes: `java.lang.Object org.package.ClassB` - Arguments.of("Ljava/lang/Object;Lorg/package/ClassB;", "Ljava/lang/Object;Lshadow/org/package/ClassB;"), - // Single method argument: `void method(org.package.ClassA arg);` - Arguments.of("(Lorg/package/ClassA;)", "(Lshadow/org/package/ClassA;)"), - // Method arguments: `void method(org.package.ClassA arg1, org.package.ClassB arg2);` - Arguments.of("(Lorg/package/ClassA;Lorg/package/ClassB;)", "(Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;)"), - // Example from issue 1403. - Arguments.of("()Lorg/package/ClassA;Lorg/package/ClassB;", "()Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;"), - ) + primitiveTypePatterns + fun signaturePatternsProvider() = + listOf( + // Normal class: `org.package.ClassA` + Arguments.of("Lorg/package/ClassA;", "Lshadow/org/package/ClassA;"), + // Array class: `org.package.ClassA[]` + Arguments.of("[Lorg/package/ClassA;", "[Lshadow/org/package/ClassA;"), + // Multidimensional array of class: `org.package.ClassA[][]` + Arguments.of("[[Lorg/package/ClassA;", "[[Lshadow/org/package/ClassA;"), + // Multiple classes: `org.package.ClassA org.package.ClassB` + Arguments.of( + "Lorg/package/ClassA;Lorg/package/ClassB;", + "Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;", + ), + // Multiple classes: `java.lang.Object org.package.ClassB` + Arguments.of( + "Ljava/lang/Object;Lorg/package/ClassB;", + "Ljava/lang/Object;Lshadow/org/package/ClassB;", + ), + // Single method argument: `void method(org.package.ClassA arg);` + Arguments.of("(Lorg/package/ClassA;)", "(Lshadow/org/package/ClassA;)"), + // Method arguments: `void method(org.package.ClassA arg1, org.package.ClassB arg2);` + Arguments.of( + "(Lorg/package/ClassA;Lorg/package/ClassB;)", + "(Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;)", + ), + // Example from issue 1403. + Arguments.of( + "()Lorg/package/ClassA;Lorg/package/ClassB;", + "()Lshadow/org/package/ClassA;Lshadow/org/package/ClassB;", + ), + ) + primitiveTypePatterns } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt index 743c6ff84..6141b1d7e 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocatorTest.kt @@ -7,7 +7,8 @@ import assertk.assertions.isTrue import org.junit.jupiter.api.Test /** - * Modified from [org.apache.maven.plugins.shade.relocation.SimpleRelocatorTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java). + * Modified from + * [org.apache.maven.plugins.shade.relocation.SimpleRelocatorTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java). */ class SimpleRelocatorTest { @@ -27,10 +28,17 @@ class SimpleRelocatorTest { assertThat(relocator.canRelocatePath("/org/Foo/Class")).isFalse() assertThat(relocator.canRelocatePath("/org/Foo/Class.class")).isFalse() - relocator = SimpleRelocator( - "org.foo", - excludes = listOf("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff"), - ) + relocator = + SimpleRelocator( + "org.foo", + excludes = + listOf( + "org.foo.Excluded", + "org.foo.public.*", + "org.foo.recurse.**", + "org.foo.Public*Stuff", + ), + ) assertThat(relocator.canRelocatePath("org/foo/Class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/excluded")).isTrue() @@ -67,7 +75,8 @@ class SimpleRelocatorTest { relocator = SimpleRelocator("foo.") assertThat(relocator.canRelocatePath("foo/foo.bar")).isTrue() relocator.exclude("foo/foo.bar") - assertThat(relocator.canRelocatePath("foo/foo.bar")).isFalse() // Don't handle file path pattern. + assertThat(relocator.canRelocatePath("foo/foo.bar")) + .isFalse() // Don't handle file path pattern. assertThat(relocator.canRelocatePath("foo/foobar")).isTrue() relocator.exclude("foo/foobar") assertThat(relocator.canRelocatePath("foo/foobar")).isFalse() // File without extension. @@ -76,10 +85,7 @@ class SimpleRelocatorTest { @Test fun canRelocatePathWithRegex() { // Include with Regex - var relocator = SimpleRelocator( - "org.foo", - includes = listOf("%regex[org/foo/R(\\$.*)?$]"), - ) + var relocator = SimpleRelocator("org.foo", includes = listOf("%regex[org/foo/R(\\$.*)?$]")) assertThat(relocator.canRelocatePath("org/foo/R.class")).isTrue() assertThat(relocator.canRelocatePath($$"org/foo/R$string.class")).isTrue() assertThat(relocator.canRelocatePath($$"org/foo/R$layout.class")).isTrue() @@ -90,10 +96,7 @@ class SimpleRelocatorTest { assertThat(relocator.canRelocatePath($$"org/R$string.class")).isFalse() // Exclude with Regex - relocator = SimpleRelocator( - "org.foo", - excludes = listOf("%regex[org/foo/.*Factory[0-9].*]"), - ) + relocator = SimpleRelocator("org.foo", excludes = listOf("%regex[org/foo/.*Factory[0-9].*]")) assertThat(relocator.canRelocatePath("org/foo/Factory.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/FooFactoryMain.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/BarFactory.class")).isTrue() @@ -102,10 +105,11 @@ class SimpleRelocatorTest { assertThat(relocator.canRelocatePath("org/foo/BarFactory2.class")).isFalse() // Include with Regex and normal pattern - relocator = SimpleRelocator( - "org.foo", - includes = listOf("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*"), - ) + relocator = + SimpleRelocator( + "org.foo", + includes = listOf("%regex[org/foo/.*Factory[0-9].*]", "org.foo.public.*"), + ) assertThat(relocator.canRelocatePath("org/foo/Factory1.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/public/Bar.class")).isTrue() assertThat(relocator.canRelocatePath("org/foo/Factory.class")).isFalse() @@ -120,10 +124,17 @@ class SimpleRelocatorTest { assertThat(relocator.canRelocateClass("com.foo.bar.Class")).isFalse() assertThat(relocator.canRelocateClass("org.Foo.Class")).isFalse() - relocator = SimpleRelocator( - "org.foo", - excludes = listOf("org.foo.Excluded", "org.foo.public.*", "org.foo.recurse.**", "org.foo.Public*Stuff"), - ) + relocator = + SimpleRelocator( + "org.foo", + excludes = + listOf( + "org.foo.Excluded", + "org.foo.public.*", + "org.foo.recurse.**", + "org.foo.Public*Stuff", + ), + ) assertThat(relocator.canRelocateClass("org.foo.Class")).isTrue() assertThat(relocator.canRelocateClass("org.foo.excluded")).isTrue() assertThat(relocator.canRelocateClass("org.foo.Excluded")).isFalse() @@ -159,11 +170,12 @@ class SimpleRelocatorTest { @Test fun canRelocateAbsClassPathWithExcludes() { - val relocator = SimpleRelocator( - "org/apache/velocity", - "org/apache/momentum", - excludes = listOf("org/apache/velocity/excluded/*"), - ) + val relocator = + SimpleRelocator( + "org/apache/velocity", + "org/apache/momentum", + excludes = listOf("org/apache/velocity/excluded/*"), + ) assertThat(relocator.canRelocatePath("/org/apache/velocity/mass.properties")).isTrue() assertThat(relocator.canRelocatePath("org/apache/velocity/mass.properties")).isTrue() assertThat(relocator.canRelocatePath("/org/apache/velocity/excluded/mass.properties")).isFalse() @@ -172,11 +184,12 @@ class SimpleRelocatorTest { @Test fun canRelocateAbsClassPathWithIncludes() { - val relocator = SimpleRelocator( - "org/apache/velocity", - "org/apache/momentum", - includes = listOf("org/apache/velocity/included/*"), - ) + val relocator = + SimpleRelocator( + "org/apache/velocity", + "org/apache/momentum", + includes = listOf("org/apache/velocity/included/*"), + ) assertThat(relocator.canRelocatePath("/org/apache/velocity/mass.properties")).isFalse() assertThat(relocator.canRelocatePath("org/apache/velocity/mass.properties")).isFalse() assertThat(relocator.canRelocatePath("/org/apache/velocity/included/mass.properties")).isTrue() @@ -200,12 +213,10 @@ class SimpleRelocatorTest { @Test fun relocateClass() { var relocator = SimpleRelocator("org.foo") - assertThat(relocator.relocateClass("org.foo.bar.Class")) - .isEqualTo("hidden.org.foo.bar.Class") + assertThat(relocator.relocateClass("org.foo.bar.Class")).isEqualTo("hidden.org.foo.bar.Class") relocator = SimpleRelocator("org.foo", "private.stuff") - assertThat(relocator.relocateClass("org.foo.bar.Class")) - .isEqualTo("private.stuff.bar.Class") + assertThat(relocator.relocateClass("org.foo.bar.Class")).isEqualTo("private.stuff.bar.Class") } @Test @@ -214,114 +225,167 @@ class SimpleRelocatorTest { assertThat(relocator.relocatePath("(I)Lorg/foo/bar/Class;")) .isEqualTo("(I)Lhidden/org/foo/bar/Class;") - relocator = SimpleRelocator("^META-INF/org.foo.xml$", "META-INF/hidden.org.foo.xml", rawString = true) + relocator = + SimpleRelocator("^META-INF/org.foo.xml$", "META-INF/hidden.org.foo.xml", rawString = true) assertThat(relocator.relocatePath("META-INF/org.foo.xml")) .isEqualTo("META-INF/hidden.org.foo.xml") } @Test fun relocateMavenFiles() { - val relocator = SimpleRelocator( - "META-INF/maven", - "META-INF/shade/maven", - excludes = listOf("META-INF/maven/com.foo.bar/artifactId/pom.*"), - ) - assertThat(relocator.canRelocatePath("META-INF/maven/com.foo.bar/artifactId/pom.properties")).isFalse() + val relocator = + SimpleRelocator( + "META-INF/maven", + "META-INF/shade/maven", + excludes = listOf("META-INF/maven/com.foo.bar/artifactId/pom.*"), + ) + assertThat(relocator.canRelocatePath("META-INF/maven/com.foo.bar/artifactId/pom.properties")) + .isFalse() assertThat(relocator.canRelocatePath("META-INF/maven/com.foo.bar/artifactId/pom.xml")).isFalse() - assertThat(relocator.canRelocatePath("META-INF/maven/com/foo/bar/artifactId/pom.properties")).isTrue() + assertThat(relocator.canRelocatePath("META-INF/maven/com/foo/bar/artifactId/pom.properties")) + .isTrue() assertThat(relocator.canRelocatePath("META-INF/maven/com/foo/bar/artifactId/pom.xml")).isTrue() - assertThat(relocator.canRelocatePath("META-INF/maven/com-foo-bar/artifactId/pom.properties")).isTrue() + assertThat(relocator.canRelocatePath("META-INF/maven/com-foo-bar/artifactId/pom.properties")) + .isTrue() assertThat(relocator.canRelocatePath("META-INF/maven/com-foo-bar/artifactId/pom.xml")).isTrue() } @Test fun canRelocateExcludedSourceFile() { - val relocator = SimpleRelocator( - "org.foo", - excludes = listOf("org/apache/iceberg/spark/parquet/**", "org/apache/spark/sql/execution/datasources/parquet/**"), - ) - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isFalse() - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class")).isFalse() - assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isFalse() + val relocator = + SimpleRelocator( + "org.foo", + excludes = + listOf( + "org/apache/iceberg/spark/parquet/**", + "org/apache/spark/sql/execution/datasources/parquet/**", + ), + ) + assertThat( + relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class") + ) + .isFalse() + assertThat( + relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class") + ) + .isFalse() + assertThat( + relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class") + ) + .isFalse() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() } @Test fun canRelocateExcludedSourceFileWithRegex() { - val relocator = SimpleRelocator( - "org.foo", - excludes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]"), - ) - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isFalse() - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class")).isFalse() - assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isFalse() + val relocator = + SimpleRelocator( + "org.foo", + excludes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]"), + ) + assertThat( + relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class") + ) + .isFalse() + assertThat( + relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class") + ) + .isFalse() + assertThat( + relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class") + ) + .isFalse() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isTrue() } @Test fun canRelocateIncludedSourceFile() { - val relocator = SimpleRelocator( - includes = listOf("org/apache/iceberg/spark/parquet/**", "org/apache/spark/sql/execution/datasources/parquet/**"), - ) - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isTrue() - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class")).isTrue() - assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isTrue() + val relocator = + SimpleRelocator( + includes = + listOf( + "org/apache/iceberg/spark/parquet/**", + "org/apache/spark/sql/execution/datasources/parquet/**", + ) + ) + assertThat( + relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class") + ) + .isTrue() + assertThat( + relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class") + ) + .isTrue() + assertThat( + relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class") + ) + .isTrue() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isFalse() } @Test fun canRelocateIncludedSourceFileWithRegex() { - val relocator = SimpleRelocator( - includes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]"), - ) - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class")).isTrue() - assertThat(relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class")).isTrue() - assertThat(relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class")).isTrue() + val relocator = + SimpleRelocator( + includes = listOf("%regex[org/apache/iceberg/.*]", "%regex[org/apache/spark/.*]") + ) + assertThat( + relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet.class") + ) + .isTrue() + assertThat( + relocator.canRelocatePath("org/apache/iceberg/spark/parquet/SparkNativeParquet$.class") + ) + .isTrue() + assertThat( + relocator.canRelocatePath("org/apache/spark/sql/execution/datasources/parquet/v1.class") + ) + .isTrue() assertThat(relocator.canRelocatePath("org/foo/Class.class")).isFalse() } @Test fun relocateSourceWithExcludesRaw() { - val relocator = SimpleRelocator( - "org.apache.maven", - "com.acme.maven", - listOf("foo.bar", "zot.baz"), - listOf("irrelevant.exclude", "org.apache.maven.exclude1", "org.apache.maven.sub.exclude2"), - true, - ) + val relocator = + SimpleRelocator( + "org.apache.maven", + "com.acme.maven", + listOf("foo.bar", "zot.baz"), + listOf("irrelevant.exclude", "org.apache.maven.exclude1", "org.apache.maven.sub.exclude2"), + true, + ) assertThat(relocator.applyToSourceContent(sourceFile)).isEqualTo(sourceFile) } @Test fun relocateSourceWithExcludes() { // Main relocator with in-/excludes - val relocator = SimpleRelocator( - "org.apache.maven", - "com.acme.maven", - listOf("foo.bar", "zot.baz"), - listOf("irrelevant.exclude", "org.apache.maven.exclude1", "org.apache.maven.sub.exclude2"), - ) + val relocator = + SimpleRelocator( + "org.apache.maven", + "com.acme.maven", + listOf("foo.bar", "zot.baz"), + listOf("irrelevant.exclude", "org.apache.maven.exclude1", "org.apache.maven.sub.exclude2"), + ) // Make sure not to replace variables 'io' and 'ioInput', package 'java.io' val ioRelocator = SimpleRelocator("io", "shaded.io") // Check corner case which was not working in PR #100 val asmRelocator = SimpleRelocator("org.objectweb.asm", "aj.org.objectweb.asm") // Make sure not to replace 'foo' package by path-like 'shaded/foo' - val fooRelocator = SimpleRelocator( - "foo", - "shaded.foo", - excludes = listOf("foo.bar"), - ) + val fooRelocator = SimpleRelocator("foo", "shaded.foo", excludes = listOf("foo.bar")) assertThat( - fooRelocator.applyToSourceContent( - asmRelocator.applyToSourceContent( - ioRelocator.applyToSourceContent(relocator.applyToSourceContent(sourceFile)), - ), - ), - ).isEqualTo(relocatedFile) + fooRelocator.applyToSourceContent( + asmRelocator.applyToSourceContent( + ioRelocator.applyToSourceContent(relocator.applyToSourceContent(sourceFile)) + ) + ) + ) + .isEqualTo(relocatedFile) } private companion object { - val sourceFile = """ + val sourceFile = + """ package org.apache.maven.hello; package org.objectweb.asm; @@ -355,9 +419,11 @@ class SimpleRelocatorTest { String relocationPath = "org/apache/maven/In"; } } - """.trimIndent() + """ + .trimIndent() - val relocatedFile = """ + val relocatedFile = + """ package com.acme.maven.hello; package aj.org.objectweb.asm; @@ -391,6 +457,7 @@ class SimpleRelocatorTest { String relocationPath = "com/acme/maven/In"; } } - """.trimIndent() + """ + .trimIndent() } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt index d3b292e30..887c479df 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheLicenseResourceTransformerTest.kt @@ -6,9 +6,11 @@ import assertk.assertions.isTrue import org.junit.jupiter.api.Test /** - * Modified from [org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformerTest.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ApacheLicenseResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheLicenseResourceTransformerTest.java). */ -class ApacheLicenseResourceTransformerTest : BaseTransformerTest() { +class ApacheLicenseResourceTransformerTest : + BaseTransformerTest() { init { setupTurkishLocale() diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt index 7f28e2582..f525b6f34 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ApacheNoticeResourceTransformerTest.kt @@ -11,7 +11,8 @@ import org.apache.tools.zip.ZipOutputStream import org.junit.jupiter.api.Test /** - * Modified from [org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformerTest.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ApacheNoticeResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ApacheNoticeResourceTransformerTest.java). */ class ApacheNoticeResourceTransformerTest : BaseTransformerTest() { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt index 514425372..7565aa624 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/AppendingTransformerTest.kt @@ -6,7 +6,8 @@ import assertk.assertions.isTrue import org.junit.jupiter.api.Test /** - * Modified from [org.apache.maven.plugins.shade.resource.AppendingTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/AppendingTransformerTest.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.AppendingTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/AppendingTransformerTest.java). */ class AppendingTransformerTest : BaseTransformerTest() { diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt index 2880784e9..5540141f2 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/BaseTransformerTest.kt @@ -27,32 +27,48 @@ abstract class BaseTransformerTest { @BeforeEach open fun beforeEach() { @Suppress("UNCHECKED_CAST") - val clazz = (this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments.single() as Class + val clazz = + (this::class.java.genericSuperclass as ParameterizedType).actualTypeArguments.single() + as Class transformer = clazz.create(testObjectFactory) } companion object { fun ResourceTransformer.canTransformResource(path: String, file: File? = null): Boolean { - val element = object : FileTreeElement by noOpDelegate() { - private val _relativePath = RelativePath.parse(true, path) - override fun getPath(): String = _relativePath.pathString - override fun getRelativePath(): RelativePath = _relativePath - override fun getFile(): File = requireNotNull(file) { "File must be provided." } - } + val element = + object : FileTreeElement by noOpDelegate() { + private val _relativePath = RelativePath.parse(true, path) + + override fun getPath(): String = _relativePath.pathString + + override fun getRelativePath(): RelativePath = _relativePath + + override fun getFile(): File = requireNotNull(file) { "File must be provided." } + } return canTransformResource(element) } fun resourceContext(path: String, vararg relocators: Relocator): TransformerContext { - return TransformerContext(path = path, inputStream = requireResourceAsStream(path), relocators = relocators.toSet()) + return TransformerContext( + path = path, + inputStream = requireResourceAsStream(path), + relocators = relocators.toSet(), + ) } - fun textContext(path: String, text: String = "", vararg relocators: Relocator): TransformerContext { - return TransformerContext(path = path, inputStream = text.byteInputStream(), relocators = relocators.toSet()) + fun textContext( + path: String, + text: String = "", + vararg relocators: Relocator, + ): TransformerContext { + return TransformerContext( + path = path, + inputStream = text.byteInputStream(), + relocators = relocators.toSet(), + ) } - fun ResourceTransformer.transformToJar( - preserveFileTimestamps: Boolean = true, - ): JarPath { + fun ResourceTransformer.transformToJar(preserveFileTimestamps: Boolean = true): JarPath { val testableZipPath = createTempFile("testable-zip-file-", ".jar") ZipOutputStream(testableZipPath.outputStream()).use { zipOutputStream -> modifyOutputStream(zipOutputStream, preserveFileTimestamps) @@ -61,12 +77,11 @@ abstract class BaseTransformerTest { } /** - * NOTE: The Turkish locale has a usual case transformation for the letters "I" and "i", making it a prime - * choice to test for improper case-less string comparisons. + * NOTE: The Turkish locale has a usual case transformation for the letters "I" and "i", making + * it a prime choice to test for improper case-less string comparisons. */ fun setupTurkishLocale() { - @Suppress("DEPRECATION") - Locale.setDefault(Locale("tr")) + @Suppress("DEPRECATION") Locale.setDefault(Locale("tr")) } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt index b503223b4..838cf710c 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ComponentsXmlResourceTransformerTest.kt @@ -8,19 +8,22 @@ import org.custommonkey.xmlunit.XMLUnit import org.junit.jupiter.api.Test /** - * Modified from [org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformerTest.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ComponentsXmlResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ComponentsXmlResourceTransformerTest.java). */ -class ComponentsXmlResourceTransformerTest : BaseTransformerTest() { +class ComponentsXmlResourceTransformerTest : + BaseTransformerTest() { @Test fun configurationMerging() { XMLUnit.setNormalizeWhitespace(true) transformer.transform(resourceContext("components-1.xml")) transformer.transform(resourceContext("components-2.xml")) - val diff = XMLUnit.compareXML( - requireResourceAsPath("components-expected.xml").readText(), - transformer.transformedResource.decodeToString(), - ) + val diff = + XMLUnit.compareXML( + requireResourceAsPath("components-expected.xml").readText(), + transformer.transformedResource.decodeToString(), + ) assertThat(diff.identical()).isTrue() } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformerTest.kt index eba93f87b..3b8e7b4cc 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/DeduplicatingResourceTransformerTest.kt @@ -14,10 +14,10 @@ import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.ValueSource -class DeduplicatingResourceTransformerTest : BaseTransformerTest() { +class DeduplicatingResourceTransformerTest : + BaseTransformerTest() { - @TempDir - lateinit var tempDir: Path + @TempDir lateinit var tempDir: Path private lateinit var file1: File private lateinit var file2: File @@ -31,15 +31,9 @@ class DeduplicatingResourceTransformerTest : BaseTransformerTest retain resource + assertThat(canTransformResource("multiple-contents", file1)).isFalse() + // same path, same file content --> skip resource + assertThat(canTransformResource("multiple-contents", file2)).isTrue() + // same path, different file content --> retain resource (even if it's a duplicate) + assertThat(canTransformResource("multiple-contents", file3)).isFalse() + + assertThat(canTransformResource("single-source", file1)).isFalse() + + assertThat(canTransformResource("same-content-twice", file1)).isFalse() + assertThat(canTransformResource("same-content-twice", file2)).isTrue() + + assertThat(canTransformResource("differing-content-2", file1)).isFalse() + assertThat(canTransformResource("differing-content-2", file3)).isFalse() + + assertThat(sources.keys) + .containsExactlyInAnyOrder( + "multiple-contents", + "single-source", + "same-content-twice", + "differing-content-2", + ) + + val pathInfosMultipleContents = sources.getValue("multiple-contents") + assertThat(pathInfosMultipleContents.failOnDuplicateContent).isEqualTo(exclusionCheck) + assertThat(pathInfosMultipleContents.uniqueContentCount()).isEqualTo(2) + assertThat(pathInfosMultipleContents.filesPerHash) + .containsOnly(hash1 to listOf(file1, file2), hash3 to listOf(file3)) + + val pathInfosSingleSource = sources.getValue("single-source") + assertThat(pathInfosSingleSource.failOnDuplicateContent).isTrue() + assertThat(pathInfosSingleSource.uniqueContentCount()).isEqualTo(1) + assertThat(pathInfosSingleSource.filesPerHash).containsOnly(hash1 to listOf(file1)) + + val pathInfosSameContentTwice = sources.getValue("same-content-twice") + assertThat(pathInfosSameContentTwice.failOnDuplicateContent).isTrue() + assertThat(pathInfosSameContentTwice.uniqueContentCount()).isEqualTo(1) + assertThat(pathInfosSameContentTwice.filesPerHash).containsOnly(hash1 to listOf(file1, file2)) + + val pathInfosDifferingContent2 = sources.getValue("differing-content-2") + assertThat(pathInfosDifferingContent2.failOnDuplicateContent).isTrue() + assertThat(pathInfosDifferingContent2.uniqueContentCount()).isEqualTo(2) + assertThat(pathInfosDifferingContent2.filesPerHash) + .containsOnly(hash1 to listOf(file1), hash3 to listOf(file3)) + + if (exclusionCheck) { + assertThat(duplicateContentViolations()) + .containsOnly( + "multiple-contents" to pathInfosMultipleContents, + "differing-content-2" to pathInfosDifferingContent2, + ) + } else { + assertThat(duplicateContentViolations()) + .containsOnly("differing-content-2" to pathInfosDifferingContent2) + } } - - // new path, new file content --> retain resource - assertThat(canTransformResource("multiple-contents", file1)).isFalse() - // same path, same file content --> skip resource - assertThat(canTransformResource("multiple-contents", file2)).isTrue() - // same path, different file content --> retain resource (even if it's a duplicate) - assertThat(canTransformResource("multiple-contents", file3)).isFalse() - - assertThat(canTransformResource("single-source", file1)).isFalse() - - assertThat(canTransformResource("same-content-twice", file1)).isFalse() - assertThat(canTransformResource("same-content-twice", file2)).isTrue() - - assertThat(canTransformResource("differing-content-2", file1)).isFalse() - assertThat(canTransformResource("differing-content-2", file3)).isFalse() - - assertThat(sources.keys).containsExactlyInAnyOrder( - "multiple-contents", - "single-source", - "same-content-twice", - "differing-content-2", - ) - - val pathInfosMultipleContents = sources.getValue("multiple-contents") - assertThat(pathInfosMultipleContents.failOnDuplicateContent).isEqualTo(exclusionCheck) - assertThat(pathInfosMultipleContents.uniqueContentCount()).isEqualTo(2) - assertThat(pathInfosMultipleContents.filesPerHash).containsOnly( - hash1 to listOf(file1, file2), - hash3 to listOf(file3), - ) - - val pathInfosSingleSource = sources.getValue("single-source") - assertThat(pathInfosSingleSource.failOnDuplicateContent).isTrue() - assertThat(pathInfosSingleSource.uniqueContentCount()).isEqualTo(1) - assertThat(pathInfosSingleSource.filesPerHash).containsOnly(hash1 to listOf(file1)) - - val pathInfosSameContentTwice = sources.getValue("same-content-twice") - assertThat(pathInfosSameContentTwice.failOnDuplicateContent).isTrue() - assertThat(pathInfosSameContentTwice.uniqueContentCount()).isEqualTo(1) - assertThat(pathInfosSameContentTwice.filesPerHash).containsOnly(hash1 to listOf(file1, file2)) - - val pathInfosDifferingContent2 = sources.getValue("differing-content-2") - assertThat(pathInfosDifferingContent2.failOnDuplicateContent).isTrue() - assertThat(pathInfosDifferingContent2.uniqueContentCount()).isEqualTo(2) - assertThat(pathInfosDifferingContent2.filesPerHash).containsOnly(hash1 to listOf(file1), hash3 to listOf(file3)) - - if (exclusionCheck) { - assertThat(duplicateContentViolations()).containsOnly( - "multiple-contents" to pathInfosMultipleContents, - "differing-content-2" to pathInfosDifferingContent2, - ) - } else { - assertThat(duplicateContentViolations()).containsOnly("differing-content-2" to pathInfosDifferingContent2) - } - } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt index 1c7305588..a6fecafd2 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/Log4j2PluginsCacheFileTransformerTest.kt @@ -28,9 +28,11 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource /** - * Modified from [org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformerTest.java](https://github.com/apache/logging-log4j-transform/blob/main/log4j-transform-maven-shade-plugin-extensions/src/test/java/org/apache/logging/log4j/maven/plugins/shade/transformer/Log4j2PluginCacheFileTransformerTest.java). + * Modified from + * [org.apache.logging.log4j.maven.plugins.shade.transformer.Log4j2PluginCacheFileTransformerTest.java](https://github.com/apache/logging-log4j-transform/blob/main/log4j-transform-maven-shade-plugin-extensions/src/test/java/org/apache/logging/log4j/maven/plugins/shade/transformer/Log4j2PluginCacheFileTransformerTest.java). */ -class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest() { +class Log4j2PluginsCacheFileTransformerTest : + BaseTransformerTest() { @Test fun canTransformResource() { assertThat(transformer.canTransformResource("")).isFalse() @@ -71,9 +73,7 @@ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest while (true) { val jarEntry = inputStream.nextJarEntry @@ -94,9 +94,8 @@ class Log4j2PluginsCacheFileTransformerTest : BaseTransformerTest() { @Test - fun canTransformResource() = with(transformer) { - append("Name", "org/foo/bar/") - append("Sealed", true) + fun canTransformResource() = + with(transformer) { + append("Name", "org/foo/bar/") + append("Sealed", true) - assertThat(canTransformResource(MANIFEST_NAME)).isTrue() - assertThat(canTransformResource(MANIFEST_NAME.lowercase())).isTrue() - } + assertThat(canTransformResource(MANIFEST_NAME)).isTrue() + assertThat(canTransformResource(MANIFEST_NAME.lowercase())).isTrue() + } @Test fun hasTransformedResource() { @@ -34,31 +35,29 @@ class ManifestAppenderTransformerTest : BaseTransformerTest() { @Test - fun defaultIncludes() = with(transformer) { - assertThat(canTransformResource("META-INF/LICENSE")).isTrue() - assertThat(canTransformResource("META-INF/LICENSE.txt")).isTrue() - assertThat(canTransformResource("META-INF/LICENSE.md")).isTrue() - assertThat(canTransformResource("LICENSE")).isTrue() - assertThat(canTransformResource("LICENSE.txt")).isTrue() - assertThat(canTransformResource("LICENSE.md")).isTrue() - assertThat(canTransformResource("something else")).isFalse() - } + fun defaultIncludes() = + with(transformer) { + assertThat(canTransformResource("META-INF/LICENSE")).isTrue() + assertThat(canTransformResource("META-INF/LICENSE.txt")).isTrue() + assertThat(canTransformResource("META-INF/LICENSE.md")).isTrue() + assertThat(canTransformResource("LICENSE")).isTrue() + assertThat(canTransformResource("LICENSE.txt")).isTrue() + assertThat(canTransformResource("LICENSE.md")).isTrue() + assertThat(canTransformResource("something else")).isFalse() + } @Test - fun customIncludes() = with(transformer) { - include("META-INF/FOO") - exclude("META-INF/LICENSE*") - exclude("LICENSE*") - assertThat(canTransformResource("META-INF/FOO")).isTrue() - assertThat(canTransformResource("META-INF/LICENSE")).isFalse() - assertThat(canTransformResource("META-INF/LICENSE.txt")).isFalse() - assertThat(canTransformResource("META-INF/LICENSE.md")).isFalse() - assertThat(canTransformResource("LICENSE")).isFalse() - assertThat(canTransformResource("LICENSE.txt")).isFalse() - assertThat(canTransformResource("LICENSE.md")).isFalse() - assertThat(canTransformResource("something else")).isFalse() - } + fun customIncludes() = + with(transformer) { + include("META-INF/FOO") + exclude("META-INF/LICENSE*") + exclude("LICENSE*") + assertThat(canTransformResource("META-INF/FOO")).isTrue() + assertThat(canTransformResource("META-INF/LICENSE")).isFalse() + assertThat(canTransformResource("META-INF/LICENSE.txt")).isFalse() + assertThat(canTransformResource("META-INF/LICENSE.md")).isFalse() + assertThat(canTransformResource("LICENSE")).isFalse() + assertThat(canTransformResource("LICENSE.txt")).isFalse() + assertThat(canTransformResource("LICENSE.md")).isFalse() + assertThat(canTransformResource("something else")).isFalse() + } @Test - fun deduplicateLicenseTexts(@TempDir tempDir: Path) = with(transformer) { - transformInternal("license one".toByteArray()) - transformInternal("\r\nlicense one\r\n".toByteArray()) - transformInternal("\nlicense one\n".toByteArray()) - transformInternal(" license two".toByteArray()) - transformInternal("\r\n\n\r\n\n license two".toByteArray()) - transformInternal(" license two\r\n\n\r\n\n".toByteArray()) - transformInternal("license three".toByteArray()) + fun deduplicateLicenseTexts(@TempDir tempDir: Path) = + with(transformer) { + transformInternal("license one".toByteArray()) + transformInternal("\r\nlicense one\r\n".toByteArray()) + transformInternal("\nlicense one\n".toByteArray()) + transformInternal(" license two".toByteArray()) + transformInternal("\r\n\n\r\n\n license two".toByteArray()) + transformInternal(" license two\r\n\n\r\n\n".toByteArray()) + transformInternal("license three".toByteArray()) - val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() - artifactLicenseFile.writeText("artifact license file content") - artifactLicense.set(artifactLicenseFile) + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) - assertThat(elements).containsExactlyInAnyOrder("license one", " license two", "license three") + assertThat(elements) + .containsExactlyInAnyOrder("license one", " license two", "license three") - assertThat(buildLicense()).isEqualTo( - """ - SPDX-License-Identifier: Apache-2.0 - artifact license file content + assertThat(buildLicense()) + .isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license file content - ------------------------------------------------------------------------------------------------------------------------ + ------------------------------------------------------------------------------------------------------------------------ - This artifact includes dependencies with the following licenses: - ---------------------------------------------------------------- + This artifact includes dependencies with the following licenses: + ---------------------------------------------------------------- - license one + license one - ------------------------------------------------------------------------------------------------------------------------ + ------------------------------------------------------------------------------------------------------------------------ - license two + license two - ------------------------------------------------------------------------------------------------------------------------ + ------------------------------------------------------------------------------------------------------------------------ - license three - """.trimIndent(), - ) - } + license three + """ + .trimIndent() + ) + } @Test - fun singleAdditionalLicense(@TempDir tempDir: Path) = with(transformer) { - transformInternal("license one".toByteArray()) + fun singleAdditionalLicense(@TempDir tempDir: Path) = + with(transformer) { + transformInternal("license one".toByteArray()) - val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() - artifactLicenseFile.writeText("artifact license file content") - artifactLicense.set(artifactLicenseFile) + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) - assertThat(elements).containsExactlyInAnyOrder("license one") + assertThat(elements).containsExactlyInAnyOrder("license one") - assertThat(buildLicense()).isEqualTo( - """ - SPDX-License-Identifier: Apache-2.0 - artifact license file content + assertThat(buildLicense()) + .isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license file content - ------------------------------------------------------------------------------------------------------------------------ + ------------------------------------------------------------------------------------------------------------------------ - This artifact includes dependencies with the following licenses: - ---------------------------------------------------------------- + This artifact includes dependencies with the following licenses: + ---------------------------------------------------------------- - license one - """.trimIndent(), - ) - } + license one + """ + .trimIndent() + ) + } @Test - fun noAdditionalLicenses(@TempDir tempDir: Path) = with(transformer) { - val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() - artifactLicenseFile.writeText("artifact license file content") - artifactLicense.set(artifactLicenseFile) - - assertThat(elements).isEmpty() - - assertThat(buildLicense()).isEqualTo( - """ - SPDX-License-Identifier: Apache-2.0 - artifact license file content - """.trimIndent(), - ) - } + fun noAdditionalLicenses(@TempDir tempDir: Path) = + with(transformer) { + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) + + assertThat(elements).isEmpty() + + assertThat(buildLicense()) + .isEqualTo( + """ + SPDX-License-Identifier: Apache-2.0 + artifact license file content + """ + .trimIndent() + ) + } @Test - fun noSpdxId(@TempDir tempDir: Path) = with(transformer) { - artifactLicenseSpdxId.unsetConvention() + fun noSpdxId(@TempDir tempDir: Path) = + with(transformer) { + artifactLicenseSpdxId.unsetConvention() - val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() - artifactLicenseFile.writeText("artifact license file content") - artifactLicense.set(artifactLicenseFile) + val artifactLicenseFile = tempDir.resolve("artifact-license").toFile() + artifactLicenseFile.writeText("artifact license file content") + artifactLicense.set(artifactLicenseFile) - assertThat(elements).isEmpty() + assertThat(elements).isEmpty() - assertThat(buildLicense()).isEqualTo( - "artifact license file content", - ) - } + assertThat(buildLicense()).isEqualTo("artifact license file content") + } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt index f6e65b7b0..bd93a42ca 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/PropertiesFileTransformerTest.kt @@ -44,9 +44,11 @@ class PropertiesFileTransformerTest : BaseTransformerTest, charset: Charset = Charsets.ISO_8859_1): TransformerContext { + fun context( + path: String, + input: Map, + charset: Charset = Charsets.ISO_8859_1, + ): TransformerContext { val properties = Properties().apply { putAll(input) } return TransformerContext(path, properties.inputStream(charset)) } @JvmStatic - fun pathProvider() = listOf( - Arguments.of("foo.properties", true), - Arguments.of("foo/bar.properties", true), - Arguments.of("foo.props", false), - ) + fun pathProvider() = + listOf( + Arguments.of("foo.properties", true), + Arguments.of("foo/bar.properties", true), + Arguments.of("foo.props", false), + ) @JvmStatic - fun charsetProvider() = listOf( - Arguments.of( - "utf8.properties", - "utf-8", - mapOf("foo" to "传傳磨宿说説"), - mapOf("foo" to "传傳磨宿说説"), - ), - ) + fun charsetProvider() = + listOf( + Arguments.of("utf8.properties", "utf-8", mapOf("foo" to "传傳磨宿说説"), mapOf("foo" to "传傳磨宿说説")) + ) @JvmStatic - fun transformConfigWithPathsProvider() = listOf( - Arguments.of( - "f.properties", - listOf("f.properties"), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo"), - ), - Arguments.of( - "foo.properties", - listOf(".*.properties"), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo"), - ), - Arguments.of( - "foo.properties", - listOf(".*bar"), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - emptyMap(), - ), - Arguments.of( - "foo.properties", - emptyList(), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo"), - ), - ) + fun transformConfigWithPathsProvider() = + listOf( + Arguments.of( + "f.properties", + listOf("f.properties"), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "foo.properties", + listOf(".*.properties"), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "foo.properties", + listOf(".*bar"), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + emptyMap(), + ), + Arguments.of( + "foo.properties", + emptyList(), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + ) @JvmStatic - fun transformConfigWithMappingsProvider() = listOf( - Arguments.of( - "f.properties", - mapOf("f.properties" to mapOf("mergeStrategy" to "first")), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo"), - ), - Arguments.of( - "f.properties", - mapOf("f.properties" to mapOf("mergeStrategy" to "latest")), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "bar"), - ), - Arguments.of( - "f.properties", - mapOf("f.properties" to mapOf("mergeStrategy" to "append")), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo,bar"), - ), - Arguments.of( - "f.properties", - mapOf("f.properties" to mapOf("mergeStrategy" to "append", "mergeSeparator" to ";")), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo;bar"), - ), - Arguments.of( - "foo.properties", - mapOf(".*.properties" to mapOf("mergeStrategy" to "first")), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo"), - ), - Arguments.of( - "foo.properties", - mapOf(".*bar" to mapOf("mergeStrategy" to "first")), - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - emptyMap(), - ), - ) + fun transformConfigWithMappingsProvider() = + listOf( + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "first")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "latest")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "bar"), + ), + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "append")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo,bar"), + ), + Arguments.of( + "f.properties", + mapOf("f.properties" to mapOf("mergeStrategy" to "append", "mergeSeparator" to ";")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo;bar"), + ), + Arguments.of( + "foo.properties", + mapOf(".*.properties" to mapOf("mergeStrategy" to "first")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + ), + Arguments.of( + "foo.properties", + mapOf(".*bar" to mapOf("mergeStrategy" to "first")), + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + emptyMap(), + ), + ) @JvmStatic - fun transformConfigProvider() = listOf( - Arguments.of( - "f.properties", - "first", - "", - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo"), - mapOf>(), - ), - Arguments.of( - "f.properties", - "latest", - "", - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "bar"), - mapOf>(), - ), - Arguments.of( - "f.properties", - "append", - ",", - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo,bar"), - mapOf>(), - ), - Arguments.of( - "f.properties", - "append", - ";", - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo;bar"), - mapOf>(), - ), - Arguments.of( - "f.properties", - "fail", - ";", - mapOf("foo" to "foo"), - mapOf("foo" to "bar"), - mapOf("foo" to "foo"), - mapOf("f.properties" to mapOf("foo" to 2)), - ), - ) + fun transformConfigProvider() = + listOf( + Arguments.of( + "f.properties", + "first", + "", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + mapOf>(), + ), + Arguments.of( + "f.properties", + "latest", + "", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "bar"), + mapOf>(), + ), + Arguments.of( + "f.properties", + "append", + ",", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo,bar"), + mapOf>(), + ), + Arguments.of( + "f.properties", + "append", + ";", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo;bar"), + mapOf>(), + ), + Arguments.of( + "f.properties", + "fail", + ";", + mapOf("foo" to "foo"), + mapOf("foo" to "bar"), + mapOf("foo" to "foo"), + mapOf("f.properties" to mapOf("foo" to 2)), + ), + ) @JvmStatic - fun keyTransformerProvider() = listOf( - Arguments.of( - "foo.properties", - { key: String -> key }, - mapOf("foo" to "bar"), - mapOf("FOO" to "baz"), - mapOf("foo" to "bar", "FOO" to "baz"), - ), - Arguments.of( - "foo.properties", - { key: String -> key.uppercase() }, - mapOf("foo" to "bar"), - mapOf("FOO" to "baz"), - mapOf("FOO" to "bar,baz"), - ), - Arguments.of( - "foo.properties", - { key: String -> "bar.${key.lowercase()}" }, - mapOf("foo" to "bar"), - mapOf("FOO" to "baz"), - mapOf("bar.foo" to "bar,baz"), - ), - Arguments.of( - "foo.properties", - { key: String -> key.replaceFirst(Regex("^(foo)"), "bar.$1") }, - mapOf("foo" to "bar"), - mapOf("FOO" to "baz"), - mapOf("bar.foo" to "bar", "FOO" to "baz"), - ), - ) + fun keyTransformerProvider() = + listOf( + Arguments.of( + "foo.properties", + { key: String -> key }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("foo" to "bar", "FOO" to "baz"), + ), + Arguments.of( + "foo.properties", + { key: String -> key.uppercase() }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("FOO" to "bar,baz"), + ), + Arguments.of( + "foo.properties", + { key: String -> "bar.${key.lowercase()}" }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("bar.foo" to "bar,baz"), + ), + Arguments.of( + "foo.properties", + { key: String -> key.replaceFirst(Regex("^(foo)"), "bar.$1") }, + mapOf("foo" to "bar"), + mapOf("FOO" to "baz"), + mapOf("bar.foo" to "bar", "FOO" to "baz"), + ), + ) } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt index dfd37491e..f41f3fa4c 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/transformers/ServiceFileTransformerTest.kt @@ -20,7 +20,8 @@ import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.MethodSource /** - * Modified from [org.apache.maven.plugins.shade.resource.ServiceResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ServiceResourceTransformerTest.java). + * Modified from + * [org.apache.maven.plugins.shade.resource.ServiceResourceTransformerTest.java](https://github.com/apache/maven-shade-plugin/blob/master/src/test/java/org/apache/maven/plugins/shade/resource/ServiceResourceTransformerTest.java). */ class ServiceFileTransformerTest : BaseTransformerTest() { private lateinit var tempJar: Path @@ -149,21 +150,23 @@ class ServiceFileTransformerTest : BaseTransformerTest() private companion object { @JvmStatic - fun resourceProvider() = listOf( - // path, exclude, expected - Arguments.of("META-INF/services/java.sql.Driver", false, true), - Arguments.of("META-INF/services/io.dropwizard.logging.AppenderFactory", false, true), - Arguments.of("META-INF/services/org.apache.maven.Shade", true, false), - Arguments.of("META-INF/services/foo/bar/moo.goo.Zoo", false, true), - Arguments.of("foo/bar.properties", false, false), - Arguments.of("foo.props", false, false), - ) + fun resourceProvider() = + listOf( + // path, exclude, expected + Arguments.of("META-INF/services/java.sql.Driver", false, true), + Arguments.of("META-INF/services/io.dropwizard.logging.AppenderFactory", false, true), + Arguments.of("META-INF/services/org.apache.maven.Shade", true, false), + Arguments.of("META-INF/services/foo/bar/moo.goo.Zoo", false, true), + Arguments.of("foo/bar.properties", false, false), + Arguments.of("foo.props", false, false), + ) @JvmStatic - fun serviceFileProvider() = listOf( - // path, input1, input2, output - Arguments.of("META-INF/services/com.acme.Foo", "foo", "bar", "foo\nbar"), - Arguments.of("META-INF/services/com.acme.Bar", "foo\nbar", "zoo", "foo\nbar\nzoo"), - ) + fun serviceFileProvider() = + listOf( + // path, input1, input2, output + Arguments.of("META-INF/services/com.acme.Foo", "foo", "bar", "foo\nbar"), + Arguments.of("META-INF/services/com.acme.Bar", "foo\nbar", "zoo", "foo\nbar\nzoo"), + ) } } diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt index 761f4522d..749af3cb9 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt @@ -9,51 +9,56 @@ import org.gradle.testkit.runner.BuildResult import org.gradle.testkit.runner.GradleRunner private val testKitDir by lazy { - val gradleUserHome = System.getenv("GRADLE_USER_HOME") - ?: Path(System.getProperty("user.home"), ".gradle").absolutePathString() + val gradleUserHome = + System.getenv("GRADLE_USER_HOME") + ?: Path(System.getProperty("user.home"), ".gradle").absolutePathString() Path(gradleUserHome, "testkit") } private val testGradleVersion by lazy { - System.getProperty("TEST_GRADLE_VERSION") ?: error("TEST_GRADLE_VERSION system property is not set.") + System.getProperty("TEST_GRADLE_VERSION") + ?: error("TEST_GRADLE_VERSION system property is not set.") } -val commonGradleArgs = setOf( - "--configuration-cache", - "--build-cache", - "--parallel", - "--stacktrace", - // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel - "-Dorg.gradle.configuration-cache.parallel=true", -) +val commonGradleArgs = + setOf( + "--configuration-cache", + "--build-cache", + "--parallel", + "--stacktrace", + // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel + "-Dorg.gradle.configuration-cache.parallel=true", + ) fun gradleRunner( projectDir: Path, arguments: Iterable, warningsAsErrors: Boolean = true, block: GradleRunner.() -> Unit = {}, -): GradleRunner = GradleRunner.create() - .withGradleVersion(testGradleVersion) - .forwardOutput() - .withPluginClasspath() - .withTestKitDir(testKitDir.toFile()) - .withArguments( - buildList { - addAll(arguments) - if (warningsAsErrors) { - add("--warning-mode=fail") +): GradleRunner = + GradleRunner.create() + .withGradleVersion(testGradleVersion) + .forwardOutput() + .withPluginClasspath() + .withTestKitDir(testKitDir.toFile()) + .withArguments( + buildList { + addAll(arguments) + if (warningsAsErrors) { + add("--warning-mode=fail") + } } - }, - ) - .withProjectDir(projectDir.toFile()) - .apply(block) + ) + .withProjectDir(projectDir.toFile()) + .apply(block) fun BuildResult.assertNoDeprecationWarnings() = apply { - assertThat(output).doesNotContain( - "has been deprecated and is scheduled to be removed in Gradle", - "has been deprecated. This is scheduled to be removed in Gradle", - "will fail with an error in Gradle", - ) + assertThat(output) + .doesNotContain( + "has been deprecated and is scheduled to be removed in Gradle", + "has been deprecated. This is scheduled to be removed in Gradle", + "will fail with an error in Gradle", + ) } // TODO: https://youtrack.jetbrains.com/issue/KT-78620 diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt index 41426f009..60302fc1e 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/JarPath.kt @@ -20,11 +20,10 @@ import kotlin.io.path.inputStream * be delegated to [JarPath] type. */ @Suppress("JavaDefaultMethodsNotOverriddenByDelegation") -class JarPath(val path: Path) : - JarFile(path.toFile()), - Path by path { +class JarPath(val path: Path) : JarFile(path.toFile()), Path by path { - val mainAttrSize: Int get() = manifest.mainAttributes.size + val mainAttrSize: Int + get() = manifest.mainAttributes.size fun getMainAttr(name: String): String? { return manifest.mainAttributes.getValue(name) @@ -42,16 +41,19 @@ fun ZipFile.getContent(entryName: String): String { } fun ZipFile.getStream(entryName: String): InputStream { - val entry = checkNotNull(getEntry(entryName)) { "Entry $entryName not found in all entries: ${entries().toList()}" } + val entry = + checkNotNull(getEntry(entryName)) { + "Entry $entryName not found in all entries: ${entries().toList()}" + } return getInputStream(entry) } fun Assert.getContent(entryName: String) = transform { it.getContent(entryName) } /** - * Scans the jar file for all entries that match the specified [entryName]. - * Unlike [getContent] or [getStream], which return only one of the matching entries - * (which one is undefined), this function returns all matching entries. + * Scans the jar file for all entries that match the specified [entryName]. Unlike [getContent] or + * [getStream], which return only one of the matching entries (which one is undefined), this + * function returns all matching entries. */ fun Assert.getContents(entryName: String) = transform { actual -> JarInputStream(actual.path.inputStream()).use { jarInput -> @@ -70,38 +72,40 @@ fun Assert.getContents(entryName: String) = transform { actual -> fun Assert.getMainAttr(name: String) = transform { it.getMainAttr(name) } /** - * Ensures the JAR contains at least the specified entries. - * Commonly used with [containsNone] to verify additional constraints. + * Ensures the JAR contains at least the specified entries. Commonly used with [containsNone] to + * verify additional constraints. */ fun Assert.containsAtLeast(vararg entries: String) = toEntries().containsAtLeast(*entries) /** - * Ensures the JAR does not contain any of the specified entries. - * Commonly used with [containsAtLeast] for stricter checks. + * Ensures the JAR does not contain any of the specified entries. Commonly used with + * [containsAtLeast] for stricter checks. */ fun Assert.containsNone(vararg entries: String) = toEntries().containsNone(*entries) /** - * Ensures the JAR contains only the specified entries. - * Used alone, without [containsAtLeast] or [containsNone]. + * Ensures the JAR contains only the specified entries. Used alone, without [containsAtLeast] or + * [containsNone]. */ fun Assert.containsOnly(vararg entries: String) = toEntries().containsOnly(*entries) /** - * Ensures the JAR contains exactly the specified entries, including duplicates, in any order. - * Used alone, without [containsAtLeast] or [containsNone]. + * Ensures the JAR contains exactly the specified entries, including duplicates, in any order. Used + * alone, without [containsAtLeast] or [containsNone]. */ -fun Assert.containsExactlyInAnyOrder(vararg entries: String) = transform { actual -> - ZipInputStream(actual.path.inputStream()).use { jarInput -> - val allEntries = mutableListOf() - while (true) { - val entry = jarInput.nextEntry ?: break - allEntries.add(entry.name) - jarInput.closeEntry() +fun Assert.containsExactlyInAnyOrder(vararg entries: String) = + transform { actual -> + ZipInputStream(actual.path.inputStream()).use { jarInput -> + val allEntries = mutableListOf() + while (true) { + val entry = jarInput.nextEntry ?: break + allEntries.add(entry.name) + jarInput.closeEntry() + } + allEntries + } } - allEntries - } -}.containsExactlyInAnyOrder(*entries) + .containsExactlyInAnyOrder(*entries) private fun Assert.toEntries() = transform { actual -> actual.entries().toList().map { it.name } diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Resource.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Resource.kt index 0f20ebee3..88a5fbad3 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Resource.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Resource.kt @@ -11,8 +11,9 @@ fun requireResourceAsStream(name: String): InputStream { } fun requireResourceAsPath(name: String): Path { - val resource = Utils::class.java.classLoader.getResource(name) - ?: throw NoSuchFileException("Resource $name not found.") + val resource = + Utils::class.java.classLoader.getResource(name) + ?: throw NoSuchFileException("Resource $name not found.") return resource.toURI().toPath() } diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Strings.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Strings.kt index 8dac7b1dd..0444763ab 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Strings.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/Strings.kt @@ -2,8 +2,10 @@ package com.github.jengelman.gradle.plugins.shadow.testkit import java.nio.file.FileSystems -val String.invariantEolString: String get() = replace(System.lineSeparator(), "\n") +val String.invariantEolString: String + get() = replace(System.lineSeparator(), "\n") -val String.variantSeparatorsPathString: String get() = replace("/", fileSystem.separator) +val String.variantSeparatorsPathString: String + get() = replace("/", fileSystem.separator) private val fileSystem = FileSystems.getDefault() From 42fb7fe886ff4901b98ed54914256cc8b2c738f4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Jan 2026 10:41:35 +0800 Subject: [PATCH 901/941] Update dependency com.facebook:ktfmt to v0.61 (#1897) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c3ed3ce93..0ec4980a1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.r androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. -ktfmt = "com.facebook:ktfmt:0.59" +ktfmt = "com.facebook:ktfmt:0.61" junit-bom = "org.junit:junit-bom:6.0.1" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" From 2bddd9628608d0e9c6921c0374b63a27e43569ab Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Tue, 6 Jan 2026 13:49:52 +0800 Subject: [PATCH 902/941] Use ASM from jdependency embedded (#1898) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/renovate.json5 | 8 -------- build.gradle.kts | 1 - docs/changes/README.md | 4 ++++ gradle/libs.versions.toml | 2 -- .../gradle/plugins/shadow/internal/RelocatorRemapper.kt | 4 ++-- .../gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 6 +++--- 6 files changed, 9 insertions(+), 16 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index e63b89d9d..85130c7ad 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -7,14 +7,6 @@ 'dependencies', ], packageRules: [ - { - // https://github.com/tcurdt/jdependency/issues/325 - groupName: 'ASM and jdependency', - matchPackageNames: [ - 'org.vafer:jdependency', - 'org.ow2.asm:asm-commons', - ], - }, { groupName: 'Develocity', matchPackageNames: [ diff --git a/build.gradle.kts b/build.gradle.kts index d335434ad..df0931c57 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -115,7 +115,6 @@ dependencies { implementation(libs.apache.commonsCodec) implementation(libs.apache.commonsIo) implementation(libs.apache.log4j) - implementation(libs.asm) implementation(libs.jdependency) implementation(libs.jdom2) implementation(libs.kotlin.metadata) diff --git a/docs/changes/README.md b/docs/changes/README.md index 307925608..d101e695d 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.0...HEAD) - 2025-xx-xx +### Fixed + +- Use ASM from jdependency embedded. ([#1898](https://github.com/GradleUp/shadow/pull/1898)) + This fixes potential classpath conflicts when using Shadow with other plugins that also use ASM. ## [9.3.0](https://github.com/GradleUp/shadow/releases/tag/9.3.0) - 2025-12-05 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0ec4980a1..04d31c4a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,8 +11,6 @@ apache-commonsCodec = "commons-codec:commons-codec:1.20.0" apache-commonsIo = "commons-io:commons-io:2.21.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.3" apache-maven-model = "org.apache.maven:maven-model:3.9.12" -asm = "org.ow2.asm:asm-commons:9.9.1" -# jdependency should be updated together with ASM, see https://github.com/tcurdt/jdependency/issues/325. jdependency = "org.vafer:jdependency:2.14" jdom2 = "org.jdom:jdom2:2.0.6.1" kotlin-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version.ref = "kotlin" } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt index ac30aeb81..c10dddab5 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/RelocatorRemapper.kt @@ -4,8 +4,8 @@ import com.github.jengelman.gradle.plugins.shadow.relocation.Relocator import com.github.jengelman.gradle.plugins.shadow.relocation.relocateClass import com.github.jengelman.gradle.plugins.shadow.relocation.relocatePath import java.util.regex.Pattern -import org.objectweb.asm.Opcodes -import org.objectweb.asm.commons.Remapper +import org.vafer.jdeb.shaded.objectweb.asm.Opcodes +import org.vafer.jdeb.shaded.objectweb.asm.commons.Remapper /** * Modified from diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index f76439721..9b8c0c933 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -32,9 +32,9 @@ import org.gradle.api.internal.file.copy.FileCopyDetailsInternal import org.gradle.api.logging.Logging import org.gradle.api.tasks.WorkResult import org.gradle.api.tasks.WorkResults -import org.objectweb.asm.ClassReader -import org.objectweb.asm.ClassWriter -import org.objectweb.asm.commons.ClassRemapper +import org.vafer.jdeb.shaded.objectweb.asm.ClassReader +import org.vafer.jdeb.shaded.objectweb.asm.ClassWriter +import org.vafer.jdeb.shaded.objectweb.asm.commons.ClassRemapper /** * Modified from From 312d8f96bd91026d3394d23c58248c48cdbc77a3 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 6 Jan 2026 13:51:27 +0800 Subject: [PATCH 903/941] Prepare version 9.3.1 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index d101e695d..fd0792495 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.0...HEAD) - 2025-xx-xx +## [9.3.1](https://github.com/GradleUp/shadow/releases/tag/9.3.1) - 2026-01-06 ### Fixed diff --git a/gradle.properties b/gradle.properties index caeded690..f6cfe0af2 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.3.1-SNAPSHOT +VERSION_NAME=9.3.1 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 666655b0235dd2fe236af6185108cb9a1cc37cb0 Mon Sep 17 00:00:00 2001 From: Goooler Date: Tue, 6 Jan 2026 13:52:42 +0800 Subject: [PATCH 904/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index fd0792495..d43674d52 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.1...HEAD) - 2026-xx-xx + + ## [9.3.1](https://github.com/GradleUp/shadow/releases/tag/9.3.1) - 2026-01-06 ### Fixed diff --git a/gradle.properties b/gradle.properties index f6cfe0af2..e553cebaa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,7 +17,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.3.1 +VERSION_NAME=9.3.2-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 01822fcb80cf1f4ebec13463712eeaa851820653 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 7 Jan 2026 08:10:31 +0800 Subject: [PATCH 905/941] Update dependency org.junit:junit-bom to v6.0.2 (#1899) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 04d31c4a4..2044b7720 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktfmt = "com.facebook:ktfmt:0.61" -junit-bom = "org.junit:junit-bom:6.0.1" +junit-bom = "org.junit:junit-bom:6.0.2" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From 122a84cb92c618b03169a7874959635a008617a5 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 8 Jan 2026 19:37:44 +0800 Subject: [PATCH 906/941] Kotlin 2.3.20-Beta1 (#1901) https://github.com/JetBrains/kotlin/releases/tag/v2.3.20-Beta1 --- build.gradle.kts | 2 +- gradle/libs.versions.toml | 2 +- .../gradle/plugins/shadow/snippet/SnippetExecutable.kt | 7 +------ .../jengelman/gradle/plugins/shadow/BasePluginTest.kt | 8 -------- .../gradle/plugins/shadow/testkit/GradleRunner.kt | 3 --- 5 files changed, 3 insertions(+), 19 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index df0931c57..256d1186b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -232,7 +232,7 @@ tasks.check { dependsOn( tasks.withType(), // TODO: https://youtrack.jetbrains.com/issue/KT-78525 - tasks.checkLegacyAbi, + tasks.checkKotlinAbi, ) } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 2044b7720..58d9b4779 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] jdkRelease = "17" minGradle = "9.0.0" -kotlin = "2.3.0" +kotlin = "2.3.20-Beta1" moshi = "1.15.2" pluginPublish = "2.0.0" diff --git a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt index 3d02c49ec..ed9b81655 100644 --- a/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt +++ b/src/documentTest/kotlin/com/github/jengelman/gradle/plugins/shadow/snippet/SnippetExecutable.kt @@ -2,7 +2,6 @@ package com.github.jengelman.gradle.plugins.shadow.snippet import com.github.jengelman.gradle.plugins.shadow.testkit.assertNoDeprecationWarnings import com.github.jengelman.gradle.plugins.shadow.testkit.gradleRunner -import com.github.jengelman.gradle.plugins.shadow.testkit.toWarningsAsErrors import java.nio.file.Path import java.util.jar.JarOutputStream import kotlin.io.path.createDirectory @@ -92,11 +91,7 @@ sealed class SnippetExecutable : Executable { } try { - gradleRunner( - projectDir = projectRoot, - arguments = listOf("build", "--stacktrace"), - warningsAsErrors = mainScript.toWarningsAsErrors(), - ) + gradleRunner(projectDir = projectRoot, arguments = listOf("build", "--stacktrace")) .build() .assertNoDeprecationWarnings() } catch (t: Throwable) { 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 416e97836..01b1a3d42 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 @@ -13,7 +13,6 @@ import com.github.jengelman.gradle.plugins.shadow.testkit.assertNoDeprecationWar import com.github.jengelman.gradle.plugins.shadow.testkit.commonGradleArgs import com.github.jengelman.gradle.plugins.shadow.testkit.gradleRunner import com.github.jengelman.gradle.plugins.shadow.testkit.requireResourceAsPath -import com.github.jengelman.gradle.plugins.shadow.testkit.toWarningsAsErrors import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer import com.github.jengelman.gradle.plugins.shadow.util.AppendableMavenRepository import com.github.jengelman.gradle.plugins.shadow.util.JarBuilder @@ -378,16 +377,9 @@ abstract class BasePluginTest { } private fun runner(arguments: Iterable, block: GradleRunner.() -> Unit): GradleRunner { - val warningsAsErrors = - try { - projectScript.readText().toWarningsAsErrors() - } catch (_: UninitializedPropertyAccessException) { - true // Default warning mode if projectScript is not initialized yet. - } return gradleRunner( projectDir = projectRoot, arguments = commonGradleArgs + arguments, - warningsAsErrors = warningsAsErrors, block = block, ) } diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt index 749af3cb9..1e58d14d2 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt @@ -60,6 +60,3 @@ fun BuildResult.assertNoDeprecationWarnings() = apply { "will fail with an error in Gradle", ) } - -// TODO: https://youtrack.jetbrains.com/issue/KT-78620 -fun String.toWarningsAsErrors(): Boolean = !contains("org.jetbrains.kotlin.multiplatform") From f80269d7cbf18c5072ccbe8186731e0260102c4d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 07:55:13 +0800 Subject: [PATCH 907/941] Update plugin android-lint to v9 (#1903) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 58d9b4779..c2afd8288 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,7 +35,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:8.13.2" +android-lint = "com.android.lint:9.0.0" jetbrains-dokka = "org.jetbrains.dokka:2.1.0" mavenPublish = "com.vanniktech.maven.publish:0.35.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From 1f569142704c85803af3f0c0089e9a91a7e24caf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 07:55:27 +0800 Subject: [PATCH 908/941] Update Develocity to v4.3.1 (#1904) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c2afd8288..c016802dc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ 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:1.0.0" -develocity = "com.gradle:develocity-gradle-plugin:4.3" +develocity = "com.gradle:develocity-gradle-plugin:4.3.1" kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 93027b77d..71e4ed189 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,7 +12,7 @@ pluginManagement { } } -plugins { id("com.gradle.develocity") version "4.3" } +plugins { id("com.gradle.develocity") version "4.3.1" } develocity { buildScan { From a56c61eeec1e0299ab6d2880e920a7a15233fe22 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 21:24:46 +0800 Subject: [PATCH 909/941] Update Gradle to v9.3.0 (#1905) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.jar | Bin 45633 -> 46175 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index f8e1ee3125fe0768e9a76ee977ac089eb657005e..61285a659d17295f1de7c53e24fdf13ad755c379 100644 GIT binary patch delta 37058 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 NNswbwE82Y!{{uvZmpcFe delta 36446 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 JWd7DQ`9Hc?h(rJY diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 23449a2b5..19a6bdeb8 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From c9d463024975db89c04c71ac91e4c893c56d790a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 19 Jan 2026 07:55:47 +0800 Subject: [PATCH 910/941] Update plugin mavenPublish to v0.36.0 (#1906) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c016802dc..34bc764f0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,6 +37,6 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } android-lint = "com.android.lint:9.0.0" jetbrains-dokka = "org.jetbrains.dokka:2.1.0" -mavenPublish = "com.vanniktech.maven.publish:0.35.0" +mavenPublish = "com.vanniktech.maven.publish:0.36.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } spotless = "com.diffplug.spotless:8.1.0" From 492fc930e589f531ce0ec30c669684c1e41619c6 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 19 Jan 2026 10:21:41 +0800 Subject: [PATCH 911/941] Don't validate Maven Central deployments (#1907) https://vanniktech.github.io/gradle-maven-publish-plugin/central/#deployment-validation --- gradle.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle.properties b/gradle.properties index e553cebaa..fd2ead277 100644 --- a/gradle.properties +++ b/gradle.properties @@ -12,6 +12,7 @@ org.gradle.parallel=true ########## Properties for publishing to Maven Central ########## mavenCentralAutomaticPublishing=true +mavenCentralDeploymentValidation=PUBLISHED mavenCentralPublishing=true signAllPublications=true From 26dfd3527b9dae9ace5cc67510feb509299ff4fc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 23 Jan 2026 09:37:06 +0800 Subject: [PATCH 912/941] Update plugin spotless to v8.2.0 (#1909) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 34bc764f0..539ce37d4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,4 +39,4 @@ android-lint = "com.android.lint:9.0.0" jetbrains-dokka = "org.jetbrains.dokka:2.1.0" mavenPublish = "com.vanniktech.maven.publish:0.36.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } -spotless = "com.diffplug.spotless:8.1.0" +spotless = "com.diffplug.spotless:8.2.0" From 0a06f8a67e55468d4e31365a281c7a29c752ba59 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 23 Jan 2026 10:06:00 +0800 Subject: [PATCH 913/941] Try ubuntu-slim (#1910) https://github.blog/changelog/2026-01-22-1-vcpu-linux-runner-now-generally-available-in-github-actions/ --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0bcae5911..7d0ec6705 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,7 +29,7 @@ jobs: build-status: needs: - build - runs-on: ubuntu-24.04-arm + runs-on: ubuntu-slim if: always() steps: - name: Check From f0bff482a31906e6bfaa2ca480cc96511f757945 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 26 Jan 2026 09:14:33 +0800 Subject: [PATCH 914/941] Update dependency org.codehaus.plexus:plexus-xml to v4.1.1 (#1911) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 539ce37d4..ebbf4b858 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -16,7 +16,7 @@ jdom2 = "org.jdom:jdom2:2.0.6.1" kotlin-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version.ref = "kotlin" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } plexus-utils = "org.codehaus.plexus:plexus-utils:4.0.2" -plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.0" +plexus-xml = "org.codehaus.plexus:plexus-xml:4.1.1" xmlunit = "org.xmlunit:xmlunit-legacy:2.11.0" moshi = { module = "com.squareup.moshi:moshi", version.ref = "moshi" } moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" } From 770d529ce2a524ae716bd871ddc80cf5a76ff330 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 28 Jan 2026 01:50:47 +0000 Subject: [PATCH 915/941] Update plugin spotless to v8.2.1 (#1912) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ebbf4b858..89a52fc82 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,4 +39,4 @@ android-lint = "com.android.lint:9.0.0" jetbrains-dokka = "org.jetbrains.dokka:2.1.0" mavenPublish = "com.vanniktech.maven.publish:0.36.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } -spotless = "com.diffplug.spotless:8.2.0" +spotless = "com.diffplug.spotless:8.2.1" From 4a8cdda8f095b2c327069586090c7e3234eaf7ab Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 08:08:54 +0800 Subject: [PATCH 916/941] Update Develocity to v4.3.2 (#1913) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 89a52fc82..65bd3d69f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ 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:1.0.0" -develocity = "com.gradle:develocity-gradle-plugin:4.3.1" +develocity = "com.gradle:develocity-gradle-plugin:4.3.2" kotlin-gradlePlugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } pluginPublish = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "pluginPublish" } diff --git a/settings.gradle.kts b/settings.gradle.kts index 71e4ed189..959437feb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,7 +12,7 @@ pluginManagement { } } -plugins { id("com.gradle.develocity") version "4.3.1" } +plugins { id("com.gradle.develocity") version "4.3.2" } develocity { buildScan { From 48aa25fbc88bfcaa2ec60b8fd99ca8661a8d433a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 11:40:05 +0000 Subject: [PATCH 917/941] Update kotlin monorepo to v2.3.20-Beta2 (#1915) * Update kotlin monorepo to v2.3.20-Beta2 * Remove checkKotlinAbi dependency --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Goooler --- build.gradle.kts | 8 +------- gradle/libs.versions.toml | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 256d1186b..c5c92129b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -228,13 +228,7 @@ tasks.validatePlugins { enableStricterValidation = true } -tasks.check { - dependsOn( - tasks.withType(), - // TODO: https://youtrack.jetbrains.com/issue/KT-78525 - tasks.checkKotlinAbi, - ) -} +tasks.check { dependsOn(tasks.withType()) } tasks.clean { delete += diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 65bd3d69f..7b6dbf0dd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] jdkRelease = "17" minGradle = "9.0.0" -kotlin = "2.3.20-Beta1" +kotlin = "2.3.20-Beta2" moshi = "1.15.2" pluginPublish = "2.0.0" From bb57f4a9da2fcf4419f00d592652ff58433f3b0a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 30 Jan 2026 07:34:19 +0800 Subject: [PATCH 918/941] Update Gradle to v9.3.1 (#1916) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 19a6bdeb8..37f78a6af 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From f4ee3b28721841714a3e9fda95a28ca6d12a4351 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 23:46:25 +0000 Subject: [PATCH 919/941] Update dependency commons-codec:commons-codec to v1.21.0 (#1917) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7b6dbf0dd..1304ba2b5 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ pluginPublish = "2.0.0" [libraries] apache-ant = "org.apache.ant:ant:1.10.15" -apache-commonsCodec = "commons-codec:commons-codec:1.20.0" +apache-commonsCodec = "commons-codec:commons-codec:1.21.0" apache-commonsIo = "commons-io:commons-io:2.21.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.3" apache-maven-model = "org.apache.maven:maven-model:3.9.12" From b6bf8d75b451684982be446087ad1acc9baadfe4 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 13 Feb 2026 21:05:07 +0800 Subject: [PATCH 920/941] Eliminate findByName for ShadowKmpPlugin (#1920) --- .../github/jengelman/gradle/plugins/shadow/ShadowKmpPlugin.kt | 3 +-- 1 file changed, 1 insertion(+), 2 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 441ad5b5d..9c3bb9591 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 @@ -17,8 +17,7 @@ public abstract class ShadowKmpPlugin : Plugin { extensions.getByType(KotlinMultiplatformExtension::class.java).targets.configureEach { target -> if (target !is KotlinJvmTarget) return@configureEach - @Suppress("EagerGradleConfiguration") - if (tasks.findByName(SHADOW_JAR_TASK_NAME) != null) { + if (tasks.names.contains(SHADOW_JAR_TASK_NAME)) { // Declaring multiple Kotlin Targets of the same type is not supported. See // https://kotl.in/declaring-multiple-targets for more details. logger.info( From 46aa4241ad0c62eefba0d3719e265c12be9c0bbb Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 13 Feb 2026 21:49:35 +0800 Subject: [PATCH 921/941] Add CONTRIBUTING.md with development workflow (#1921) * Initial plan * Add CONTRIBUTING.md with development commands and contribution guidelines Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> * Delete .github/CONTRIBUTING.md * Apply suggestions from code review * Update CONTRIBUTING.md * Apply suggestions from code review * Update CONTRIBUTING.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Add agent guidelines for contributing to the repository --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> Co-authored-by: Zongle Wang Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/CONTRIBUTING.md | 3 -- AGENTS.md | 9 ++++++ CONTRIBUTING.md | 72 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 3 deletions(-) delete mode 100644 .github/CONTRIBUTING.md create mode 100644 AGENTS.md create mode 100644 CONTRIBUTING.md diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index 7abbcca76..000000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,3 +0,0 @@ -# Contributing to Shadow Gradle Plugin - -Thank you for considering contributing diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..83cc35c3b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,9 @@ +# Agent Guidelines + +To understand how to contribute code, format commits, and run tests for this repository, +please refer to the [CONTRIBUTING.md](./CONTRIBUTING.md) file. + +**Instructions for AI Agents:** +1. Read `CONTRIBUTING.md` before suggesting any code changes. +2. Adhere to the coding standards and PR templates defined therein. +3. Follow the environment setup steps to verify your proposed fixes. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..bab7e4341 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,72 @@ +# Contributing to Shadow + +Thank you for considering contributing to the Shadow Gradle plugin! This document provides guidelines and information to help you get started. + +## Development Commands + +### Code Style + +Shadow uses [Spotless](https://github.com/diffplug/spotless) to maintain consistent code formatting. + +- **Check code style**: `./gradlew spotlessCheck` +- **Format code**: `./gradlew spotlessFormat` + +Please ensure your code is properly formatted before submitting a pull request. + +### Testing + +Shadow has multiple test suites to ensure code quality: + +- **Unit tests**: `./gradlew test` +- **Documentation tests**: `./gradlew documentTest` - Tests code snippets in the `./docs` directory +- **Functional/Integration tests**: `./gradlew functionalTest` + +Make sure all tests pass before submitting your changes. + +### API Compatibility + +Shadow uses Kotlin's [binary compatibility validator](https://kotlinlang.org/docs/gradle-binary-compatibility-validation.html) to track public API changes. + +- **Check API dumps**: `./gradlew checkKotlinAbi` +- **Update API dumps**: `./gradlew updateKotlinAbi` + +If you add or modify public APIs, you'll need to update the API dump files. + +### Linting + +Shadow uses [Android Lint](https://developer.android.com/studio/write/lint) to catch potential issues. + +- **Run lint checks**: `./gradlew lint` +- **Update lint baseline**: `./gradlew updateLintBaseline` + +## Contribution Guidelines + +### Fixing Issues + +When fixing bugs or issues: + +1. Ensure all existing tests pass +2. Add regression tests that verify the fix for the reported issue +3. Run the full test suite to ensure no unintended side effects +4. Update documentation if the fix changes behavior + +### Adding New Features or APIs + +When adding new features or public APIs: + +1. Ensure proper visibility modifiers are used +2. All public APIs must be documented with KDoc comments +3. Run `./gradlew updateKotlinAbi` to update the API dump files +4. Add appropriate tests for the new functionality +5. Update the documentation in the `./docs` directory if applicable + +### Before Submitting a Pull Request + +1. Run `./gradlew spotlessFormat` to format your code +2. Run `./gradlew spotlessCheck` to verify formatting +3. Run all test suites: `./gradlew test documentTest functionalTest` +4. Run `./gradlew checkKotlinAbi` to ensure API compatibility +5. Run `./gradlew lint` to check for potential issues +6. Optionally, run `./gradlew build` to run compilation, tests, and standard verification tasks configured for the project +7. Ensure your commit messages are clear and descriptive + From cd29dcdbf5f68a21f73c8b6a6e7be97f3bc56d09 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 13 Feb 2026 18:43:28 +0000 Subject: [PATCH 922/941] Update plugin android-lint to v9.0.1 (#1922) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1304ba2b5..614550a65 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,7 +35,7 @@ assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } -android-lint = "com.android.lint:9.0.0" +android-lint = "com.android.lint:9.0.1" jetbrains-dokka = "org.jetbrains.dokka:2.1.0" mavenPublish = "com.vanniktech.maven.publish:0.36.0" pluginPublish = { id = "com.gradle.plugin-publish", version.ref = "pluginPublish" } From 24ba8ca222a02b9074f858601177d36c76998516 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 16 Feb 2026 09:44:06 +0800 Subject: [PATCH 923/941] Update dependency org.junit:junit-bom to v6.0.3 (#1923) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 614550a65..402ffda67 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ androidx-gradlePluginLints = "androidx.lint:lint-gradle:1.0.0-alpha05" # Dummy to get renovate updates, the version is used in rootProject build.gradle with spotless. ktfmt = "com.facebook:ktfmt:0.61" -junit-bom = "org.junit:junit-bom:6.0.2" +junit-bom = "org.junit:junit-bom:6.0.3" assertk = "com.willowtreeapps.assertk:assertk:0.28.1" [plugins] From bb11389d60e57d6781d3edad855807ea2b4ba20f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 19 Feb 2026 13:20:36 +0800 Subject: [PATCH 924/941] Gradle 9.4.0-rc-1 (#1919) https://docs.gradle.org/9.4.0-rc-1/release-notes.html#tooling-and-ide-integration --- build.gradle.kts | 10 ++----- gradle.properties | 1 + gradle/wrapper/gradle-wrapper.jar | Bin 46175 -> 48966 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- .../gradle/plugins/shadow/BasePluginTest.kt | 2 +- .../gradle/plugins/shadow/JavaPluginsTest.kt | 12 ++++++-- .../gradle/plugins/shadow/ShadowJavaPlugin.kt | 21 ++------------ .../plugins/shadow/internal/GradleCompat.kt | 26 ++++++++++++++++++ .../plugins/shadow/ShadowPropertiesTest.kt | 5 +++- .../plugins/shadow/testkit/GradleRunner.kt | 2 +- 11 files changed, 49 insertions(+), 34 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index c5c92129b..8d9204d65 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -196,8 +196,7 @@ gradlePlugin { vcsUrl = providers.gradleProperty("POM_URL") plugins { - create("shadowPlugin") { - id = "com.gradleup.shadow" + create("com.gradleup.shadow") { implementationClass = "com.github.jengelman.gradle.plugins.shadow.ShadowPlugin" displayName = providers.gradleProperty("POM_NAME").get() description = providers.gradleProperty("POM_DESCRIPTION").get() @@ -205,7 +204,7 @@ gradlePlugin { } } - testSourceSets(sourceSets["functionalTest"], sourceSets["documentTest"]) + testSourceSets(sourceSets["test"], sourceSets["functionalTest"], sourceSets["documentTest"]) } // This part should be placed after testing.suites to ensure the test sourceSets are created. @@ -223,11 +222,6 @@ tasks.withType().configureEach { tasks.pluginUnderTestMetadata { pluginClasspath.from(testPluginClasspath) } -tasks.validatePlugins { - // TODO: https://github.com/gradle/gradle/issues/22600 - enableStricterValidation = true -} - tasks.check { dependsOn(tasks.withType()) } tasks.clean { diff --git a/gradle.properties b/gradle.properties index fd2ead277..e000468dc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,6 +8,7 @@ org.gradle.configuration-cache.parallel=true org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g org.gradle.kotlin.dsl.allWarningsAsErrors=true org.gradle.parallel=true +org.gradle.tooling.parallel=true ########## Properties for publishing to Maven Central ########## diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 61285a659d17295f1de7c53e24fdf13ad755c379..d997cfc60f4cff0e7451d19d49a82fa986695d07 100644 GIT binary patch delta 39855 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

SdO`o?U^bCPz6Ehh!k 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


Xdyd*p!l1Y@nCA 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37f78a6af..ec2cf4d7b 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-rc-1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index adff685a0..fb9824e07 100755 --- a/gradlew +++ b/gradlew @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/a2da7f311fe4699328dbcef381bc459c2f757e3e/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. 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 01b1a3d42..b69e5b854 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 @@ -350,7 +350,7 @@ abstract class BasePluginTest { ${getDefaultProjectBuildScript("java-gradle-plugin")} gradlePlugin { plugins { - create('myPlugin') { + create('my.plugin') { id = 'my.plugin' implementationClass = 'my.plugin.MyPlugin' } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index b1016fa16..0897a3f0e 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -23,6 +23,7 @@ import com.github.jengelman.gradle.plugins.shadow.testkit.containsOnly import com.github.jengelman.gradle.plugins.shadow.testkit.getContent import com.github.jengelman.gradle.plugins.shadow.testkit.getMainAttr import com.github.jengelman.gradle.plugins.shadow.testkit.getStream +import com.github.jengelman.gradle.plugins.shadow.testkit.testGradleVersion import com.github.jengelman.gradle.plugins.shadow.util.Issue import com.github.jengelman.gradle.plugins.shadow.util.prependText import com.github.jengelman.gradle.plugins.shadow.util.runProcess @@ -34,11 +35,13 @@ import kotlin.io.path.writeText import kotlin.reflect.full.declaredFunctions import kotlin.reflect.jvm.javaMethod import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.IMPLEMENTATION_CONFIGURATION_NAME import org.gradle.api.tasks.bundling.ZipEntryCompression import org.gradle.language.base.plugins.LifecycleBasePlugin.ASSEMBLE_TASK_NAME import org.gradle.testkit.runner.TaskOutcome.SUCCESS +import org.gradle.util.GradleVersion import org.junit.jupiter.api.Test import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments @@ -515,11 +518,16 @@ class JavaPluginsTest : BasePluginTest() { projectScript.writeText(getDefaultProjectBuildScript("java-gradle-plugin")) val outputCompileOnly = dependencies(COMPILE_ONLY_CONFIGURATION_NAME) + val outputCompileOnlyApi = dependencies(COMPILE_ONLY_API_CONFIGURATION_NAME) val outputApi = dependencies(API_CONFIGURATION_NAME) // "unspecified" is the local Gradle API. - assertThat(outputCompileOnly).contains("unspecified") - assertThat(outputApi).doesNotContain("unspecified") + if (GradleVersion.version(testGradleVersion) >= GradleVersion.version("9.4.0-rc-1")) { + assertThat(outputCompileOnlyApi).contains("unspecified") + } else { + assertThat(outputCompileOnly).contains("unspecified") + assertThat(outputApi).doesNotContain("unspecified") + } } @Issue("https://github.com/GradleUp/shadow/issues/1422") 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 55c86d5da..3be275cd3 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 @@ -4,6 +4,7 @@ import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.SHA import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow import com.github.jengelman.gradle.plugins.shadow.internal.addVariantsFromConfigurationCompat import com.github.jengelman.gradle.plugins.shadow.internal.javaPluginExtension +import com.github.jengelman.gradle.plugins.shadow.internal.moveGradleApiIntoCompileOnly 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.Companion.registerShadowJarCommon @@ -21,9 +22,7 @@ import org.gradle.api.attributes.Usage import org.gradle.api.attributes.java.TargetJvmVersion import org.gradle.api.component.AdhocComponentWithVariants import org.gradle.api.component.SoftwareComponentFactory -import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME -import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.tasks.bundling.Jar public abstract class ShadowJavaPlugin @@ -141,23 +140,7 @@ constructor(private val softwareComponentFactory: SoftwareComponentFactory) : Pl } protected open fun Project.configureJavaGradlePlugin() { - // org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin - plugins.withId("org.gradle.java-gradle-plugin") { - val gradleApi = dependencies.gradleApi() - // Remove the gradleApi so it isn't merged into the jar file. - // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. - // See - // https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161. - configurations.named(API_CONFIGURATION_NAME) { api -> - // Only proceed if the removal is successful. - if (!api.dependencies.remove(gradleApi)) return@named - // Compile only gradleApi() to make sure the plugin can compile against Gradle API. - configurations - .getByName(COMPILE_ONLY_CONFIGURATION_NAME) - .dependencies - .add(dependencies.gradleApi()) - } - } + moveGradleApiIntoCompileOnly() } public companion object { diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt index ae7f649e3..bdccf7d3e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/GradleCompat.kt @@ -15,6 +15,8 @@ import org.gradle.api.model.ObjectFactory import org.gradle.api.plugins.ExtraPropertiesExtension import org.gradle.api.plugins.JavaApplication import org.gradle.api.plugins.JavaPlugin +import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.provider.MapProperty import org.gradle.api.provider.Property @@ -79,6 +81,30 @@ internal fun AdhocComponentWithVariants.addVariantsFromConfigurationCompat( } } +/** TODO: this could be removed after bumping the min Gradle requirement to 9.4 or above. */ +internal fun Project.moveGradleApiIntoCompileOnly() { + // gradleApi has been added into compileOnlyApi since Gradle 9.4-rc-1. + if (GradleVersion.current() >= GradleVersion.version("9.4.0-rc-1")) return + + // org.gradle.plugin.devel.plugins.JavaGradlePluginPlugin + plugins.withId("org.gradle.java-gradle-plugin") { + val gradleApi = dependencies.gradleApi() + // Remove the gradleApi so it isn't merged into the jar file. + // This is required because 'java-gradle-plugin' adds gradleApi() to the 'api' configuration. + // See + // https://github.com/gradle/gradle/blob/972c3e5c6ef990dd2190769c1ce31998a9402a79/subprojects/plugin-development/src/main/java/org/gradle/plugin/devel/plugins/JavaGradlePluginPlugin.java#L161. + configurations.named(API_CONFIGURATION_NAME) { api -> + // Only proceed if the removal is successful. + if (!api.dependencies.remove(gradleApi)) return@named + // Compile only gradleApi() to make sure the plugin can compile against Gradle API. + configurations + .getByName(COMPILE_ONLY_CONFIGURATION_NAME) + .dependencies + .add(dependencies.gradleApi()) + } + } +} + internal inline fun ObjectFactory.property( defaultValue: Any? = null ): Property = diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index 803ca4ba4..38b38a1f6 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -31,6 +31,7 @@ import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.plugins.ApplicationPlugin import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME +import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_API_CONFIGURATION_NAME import org.gradle.api.plugins.JavaPlugin.COMPILE_ONLY_CONFIGURATION_NAME import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider @@ -254,9 +255,11 @@ class ShadowPropertiesTest { plugins.apply(JavaGradlePluginPlugin::class.java) val api = configurations.named(API_CONFIGURATION_NAME).get() val compileOnly = configurations.named(COMPILE_ONLY_CONFIGURATION_NAME).get() + val compileOnlyApi = configurations.named(COMPILE_ONLY_API_CONFIGURATION_NAME).get() val gradleApi = dependencies.gradleApi() assertThat(api.dependencies).containsNone(gradleApi) - assertThat(compileOnly.dependencies).containsOnly(gradleApi) + assertThat(compileOnly.dependencies).containsNone(gradleApi) + assertThat(compileOnlyApi.dependencies).containsOnly(gradleApi) } private companion object { diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt index 1e58d14d2..3b5707575 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt @@ -15,7 +15,7 @@ private val testKitDir by lazy { Path(gradleUserHome, "testkit") } -private val testGradleVersion by lazy { +val testGradleVersion: String by lazy { System.getProperty("TEST_GRADLE_VERSION") ?: error("TEST_GRADLE_VERSION system property is not set.") } From a07c8a4037248caa7aafa77639af380d04fb028c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 19 Feb 2026 13:01:18 +0000 Subject: [PATCH 925/941] Update kotlin monorepo to v2.3.20-RC (#1925) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 402ffda67..9667c29b1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] jdkRelease = "17" minGradle = "9.0.0" -kotlin = "2.3.20-Beta2" +kotlin = "2.3.20-RC" moshi = "1.15.2" pluginPublish = "2.0.0" From 24cc83a80081e43e87b0b6617f279fb9c103410b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 25 Feb 2026 07:56:11 +0800 Subject: [PATCH 926/941] Update dependency org.vafer:jdependency to v2.15 (#1926) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9667c29b1..ef08ca39e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -11,7 +11,7 @@ apache-commonsCodec = "commons-codec:commons-codec:1.21.0" apache-commonsIo = "commons-io:commons-io:2.21.0" apache-log4j = "org.apache.logging.log4j:log4j-core:2.25.3" apache-maven-model = "org.apache.maven:maven-model:3.9.12" -jdependency = "org.vafer:jdependency:2.14" +jdependency = "org.vafer:jdependency:2.15" jdom2 = "org.jdom:jdom2:2.0.6.1" kotlin-metadata = { module = "org.jetbrains.kotlin:kotlin-metadata-jvm", version.ref = "kotlin" } kotlin-reflect = { module = "org.jetbrains.kotlin:kotlin-reflect", version.ref = "kotlin" } From 6dd58e268a27cb2b869ad02d88e3811d7271f503 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 26 Feb 2026 14:31:36 +0800 Subject: [PATCH 927/941] Sync changelog for main from 8.3.10 release (#1928) * Initial plan * Sync changelog for main from 8.3.10 release Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> --- docs/changes/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/changes/README.md b/docs/changes/README.md index d43674d52..26b353a21 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -386,6 +386,19 @@ If you used Shadow for merging service files, the following steps are recommende See more details about the fixed `DuplicatesStrategy` behaviors at [Handling Duplicates Strategy](https://gradleup.com/shadow/configuration/merging/#handling-duplicates-strategy). +## [8.3.10](https://github.com/GradleUp/shadow/releases/tag/8.3.10) - 2026-02-26 + +**Changed** + +- Stop using start script templates bundled in Shadow. ([#1750](https://github.com/GradleUp/shadow/pull/1750)) +- Update ASM and jdependency to support Java 26. ([#1810](https://github.com/GradleUp/shadow/pull/1810)) + +**Fixed** + +- Fix resolving BOM dependencies when `minimize` is enabled. ([#1638](https://github.com/GradleUp/shadow/pull/1638)) +- Use ASM from jdependency embedded. ([#1898](https://github.com/GradleUp/shadow/pull/1898)) + This fixes potential classpath conflicts when using Shadow with other plugins that also use ASM. + ## [8.3.9](https://github.com/GradleUp/shadow/releases/tag/8.3.9) - 2025-08-05 **Changed** From a0095134cc4d38c9e0becdb382f4622dd7c79c8c Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Thu, 26 Feb 2026 18:56:19 +0800 Subject: [PATCH 928/941] Update README to reflect changes in module descriptors --- docs/changes/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 26b353a21..8ec421378 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -84,8 +84,8 @@ ### Changed -- Merge Gradle Module descriptors into the modern `META-INF` path. ([#1706](https://github.com/GradleUp/shadow/pull/1706)) - The Gradle Module descriptors (`org.codehaus.groovy.runtime.ExtensionModule` files) defined under `META-INF/services/` +- Merge Groovy Module descriptors into the modern `META-INF` path. ([#1706](https://github.com/GradleUp/shadow/pull/1706)) + The Groovy Module descriptors (`org.codehaus.groovy.runtime.ExtensionModule` files) defined under `META-INF/services/` and `META-INF/groovy` will be merged into `META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule`. - Move injecting `Class-Path` manifest attr logic from `doFirst` into `copy`. ([#1720](https://github.com/GradleUp/shadow/pull/1720)) - Move injecting `Main-Class` manifest attr logic from `doFirst` into `copy`. ([#1724](https://github.com/GradleUp/shadow/pull/1724)) From 634f2462da727d623e883600e2a5e133c34fa7a7 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 02:10:44 +0000 Subject: [PATCH 929/941] Log warnings for duplicates in the final JAR (#1931) * Initial plan * warn on duplicate entries instead of info level logging Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> * Update README with duplicate log warning details Added a note about logging warnings for duplicates in the final JAR. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> Co-authored-by: Zongle Wang --- docs/changes/README.md | 3 +++ .../github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt | 4 ++-- .../jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 8ec421378..2aac40923 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.1...HEAD) - 2026-xx-xx +### Changed + +- Log warnings for duplicates in the final JAR. ([#1931](https://github.com/GradleUp/shadow/pull/1931)) ## [9.3.1](https://github.com/GradleUp/shadow/releases/tag/9.3.1) - 2026-01-06 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 0897a3f0e..f2d39283b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -884,7 +884,7 @@ class JavaPluginsTest : BasePluginTest() { if (enable) { runWithFailure(shadowJarPath) } else { - runWithSuccess(shadowJarPath, infoArgument) + runWithSuccess(shadowJarPath) } assertThat(result.output) @@ -911,7 +911,7 @@ class JavaPluginsTest : BasePluginTest() { if (enable) { runWithFailure(shadowJarPath, "--fail-on-duplicate-entries") } else { - runWithSuccess(shadowJarPath, infoArgument, "--no-fail-on-duplicate-entries") + runWithSuccess(shadowJarPath, "--no-fail-on-duplicate-entries") } assertThat(result.output) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt index 9b8c0c933..b965e0459 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowCopyAction.kt @@ -144,7 +144,7 @@ constructor( if (failOnDuplicateEntries) { throw GradleException(message) } else { - logger.info(message) + logger.warn(message) } } } From db1a2d828eaed8022c8d2e8968a4961e90a12bdb Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:28:08 +0800 Subject: [PATCH 930/941] Fix relocation patterns not included in task fingerprint (#1933) * Initial plan * Fix issue 1932: Add @Input annotations to relocation pattern fields in SimpleRelocator Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> * Mark internal * Update changelog * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> Co-authored-by: Goooler Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/changes/README.md | 4 +++ .../gradle/plugins/shadow/CachingTest.kt | 32 +++++++++++++++++++ .../shadow/relocation/SimpleRelocator.kt | 11 +++---- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 2aac40923..18bd66527 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -7,6 +7,10 @@ - Log warnings for duplicates in the final JAR. ([#1931](https://github.com/GradleUp/shadow/pull/1931)) +### Fixed + +- Fix relocation patterns not included in task fingerprint. ([#1933](https://github.com/GradleUp/shadow/pull/1933)) + ## [9.3.1](https://github.com/GradleUp/shadow/releases/tag/9.3.1) - 2026-01-06 ### Fixed diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt index e80cb6010..39501d60b 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/CachingTest.kt @@ -373,6 +373,38 @@ class CachingTest : BasePluginTest() { } } + @Issue("https://github.com/GradleUp/shadow/issues/1932") + @Test + fun relocatorPatternChanged() { + projectScript.appendText( + """ + dependencies { + implementation 'junit:junit:3.8.2' + } + $shadowJarTask { + relocate 'junit.framework', 'foo.junit.framework' + } + """ + .trimIndent() + lineSeparator + ) + val mainClassEntry = writeClass(withImports = true) + val fooEntries = + junitEntries.map { it.replace("junit/framework/", "foo/junit/framework/") }.toTypedArray() + + assertCompositeExecutions { + containsOnly("my/", "foo/", "foo/junit/", mainClassEntry, *fooEntries, *manifestEntries) + } + + val replaced = projectScript.readText().replace("foo.junit.framework", "bar.junit.framework") + projectScript.writeText(replaced) + val barEntries = + junitEntries.map { it.replace("junit/framework/", "bar/junit/framework/") }.toTypedArray() + + assertCompositeExecutions { + containsOnly("my/", "bar/", "bar/junit/", mainClassEntry, *barEntries, *manifestEntries) + } + } + @Test fun serviceFileTransformerPropsChanged() { val mainClassEntry = writeClass() diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt index 26676f766..9d624a918 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/relocation/SimpleRelocator.kt @@ -22,18 +22,17 @@ constructor( shadedPattern: String? = null, includes: List? = null, excludes: List? = null, - private val rawString: Boolean = false, + @get:Input internal val rawString: Boolean = false, @get:Input override var skipStringConstants: Boolean = false, ) : Relocator { - private val pattern: String - private val pathPattern: String - private val shadedPattern: String - private val shadedPathPattern: String + @get:Input internal val pattern: String + @get:Input internal val pathPattern: String + @get:Input internal val shadedPattern: String + @get:Input internal val shadedPathPattern: String private val sourcePackageExcludes = mutableSetOf() private val sourcePathExcludes = mutableSetOf() @get:Input public val includes: MutableSet = mutableSetOf() - @get:Input public val excludes: MutableSet = mutableSetOf() init { From 0b52ba83f5ffee0dd5517d607a0d41be54bbe3ed Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 11:43:42 +0000 Subject: [PATCH 931/941] Update Gradle to v9.4.0-rc-2 (#1934) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ec2cf4d7b..b8635c8a6 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-rc-1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-rc-2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index fb9824e07..7fadebc59 100755 --- a/gradlew +++ b/gradlew @@ -57,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/a2da7f311fe4699328dbcef381bc459c2f757e3e/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/b5fe9efed6cae7b9f2fbdb2d380fb69af16bb752/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. From 6272e523ea36b7dbb64dad8e9bdf0b7d63c6c053 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:50:51 +0800 Subject: [PATCH 932/941] Mention updating the unreleased changelog section (#1935) * Initial plan * Update CONTRIBUTING.md to mention updating Unreleased changelog section Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bab7e4341..175703264 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -69,4 +69,5 @@ When adding new features or public APIs: 5. Run `./gradlew lint` to check for potential issues 6. Optionally, run `./gradlew build` to run compilation, tests, and standard verification tasks configured for the project 7. Ensure your commit messages are clear and descriptive +8. Update the `Unreleased` section in [CHANGELOG](docs/changes/README.md) if applicable From 929f027974dca7ee2bab7269dc58629fc8cfc8e6 Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 27 Feb 2026 20:03:03 +0800 Subject: [PATCH 933/941] Prepare version 9.3.2 --- docs/changes/README.md | 2 +- gradle.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 18bd66527..e713fcfd2 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.1...HEAD) - 2026-xx-xx +## [9.3.2](https://github.com/GradleUp/shadow/releases/tag/9.3.2) - 2026-02-27 ### Changed diff --git a/gradle.properties b/gradle.properties index e000468dc..e89f79edc 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.3.2-SNAPSHOT +VERSION_NAME=9.3.2 POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From 2fa389409e2f6508f2d50fbbfd9b7feec262c56a Mon Sep 17 00:00:00 2001 From: Goooler Date: Fri, 27 Feb 2026 20:03:46 +0800 Subject: [PATCH 934/941] Prepare next development version --- docs/changes/README.md | 3 +++ gradle.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index e713fcfd2..d519966c6 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,6 +1,9 @@ # Change Log +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.3...HEAD) - 2026-xx-xx + + ## [9.3.2](https://github.com/GradleUp/shadow/releases/tag/9.3.2) - 2026-02-27 ### Changed diff --git a/gradle.properties b/gradle.properties index e89f79edc..5571383c8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -19,7 +19,7 @@ signAllPublications=true GROUP=com.gradleup.shadow POM_ARTIFACT_ID=shadow-gradle-plugin -VERSION_NAME=9.3.2 +VERSION_NAME=9.3.3-SNAPSHOT POM_NAME=Shadow Gradle Plugin POM_DESCRIPTION=Gradle plugin to create fat/uber JARs, apply file transforms, and relocate packages for applications and libraries. Gradle version of Maven's Shade plugin. From f8d019b064ea4726398ba6aca5c6731b83d881ed Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Fri, 27 Feb 2026 20:22:25 +0800 Subject: [PATCH 935/941] Change Maven Central deployment validation to NONE --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 5571383c8..1bf430bd6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,7 @@ org.gradle.tooling.parallel=true ########## Properties for publishing to Maven Central ########## mavenCentralAutomaticPublishing=true -mavenCentralDeploymentValidation=PUBLISHED +mavenCentralDeploymentValidation=NONE mavenCentralPublishing=true signAllPublications=true From 20adfb36c211f2da4e4f64f306e05e9f559e708f Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 1 Mar 2026 18:33:26 +0800 Subject: [PATCH 936/941] Update unreleased version link in CHANGELOG --- docs/changes/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index d519966c6..9575fa21e 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -1,7 +1,7 @@ # Change Log -## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.3...HEAD) - 2026-xx-xx +## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.2...HEAD) - 2026-xx-xx ## [9.3.2](https://github.com/GradleUp/shadow/releases/tag/9.3.2) - 2026-02-27 From 29cbc564e10d2aaa0a38b3ebc22b39eebc9a990a Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 1 Mar 2026 19:36:15 +0800 Subject: [PATCH 937/941] Fix interaction with Gradle artifact transforms (#1345) * Test `compatGradleArtifactTransform` * Tweak `compatGradleArtifactTransform` * Update `resolve` * Call `project.provider` * Update changelog * Clean up test project * Update docs/changes/README.md --- docs/changes/README.md | 3 + .../gradle/plugins/shadow/JavaPluginsTest.kt | 105 ++++++++++++++++++ .../plugins/shadow/tasks/DependencyFilter.kt | 22 ++-- 3 files changed, 121 insertions(+), 9 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 9575fa21e..4379f310f 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,9 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.2...HEAD) - 2026-xx-xx +### Fixed + +- Fix interaction with Gradle artifact transforms. ([#1345](https://github.com/GradleUp/shadow/pull/1345)) ## [9.3.2](https://github.com/GradleUp/shadow/releases/tag/9.3.2) - 2026-02-27 diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index f2d39283b..43d7cc948 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -948,6 +948,111 @@ class JavaPluginsTest : BasePluginTest() { assertThat(outputShadowedJar).useAll { getMainAttr(mainClassAttributeKey).isEqualTo(expected) } } + @Issue("https://github.com/GradleUp/shadow/issues/882") + @Test + fun compatGradleArtifactTransform() { + settingsScript.writeText("include('app', 'lib')\n") + path("lib/build.gradle") + .writeText( + """ + plugins { + id 'java-library' + } + """ + .trimIndent() + ) + path("lib/src/main/java/com/company/Utils.java") + .writeText( + """ + package com.company; + + public class Utils { + public static void foo() { + System.out.println("bar"); + } + } + """ + .trimIndent() + ) + path("app/build.gradle") + .writeText( + """ + import org.gradle.api.artifacts.transform.TransformParameters + import org.gradle.api.artifacts.transform.TransformAction + import org.gradle.api.artifacts.transform.TransformOutputs + import org.gradle.api.artifacts.transform.InputArtifact + import org.gradle.api.file.FileSystemLocation + import org.gradle.api.provider.Provider + + plugins { + id 'application' + id '$shadowPluginId' + } + + application { + mainClass = 'com.company.Main' + } + + dependencies { + implementation project(':lib') + } + + def transformedAttribute = Attribute.of('custom-transformed', Boolean) + + dependencies { + attributesSchema { + attribute(transformedAttribute) + } + artifactTypes.maybeCreate('jar').attributes.attribute(transformedAttribute, false) + } + + dependencies { + registerTransform(CustomTransformAction) { + from.attribute(Attribute.of('artifactType', String), 'jar').attribute(transformedAttribute, false) + to.attribute(Attribute.of('artifactType', String), 'jar').attribute(transformedAttribute, true) + } + } + + $shadowJarTask { + configurations = [project.configurations.runtimeClasspath] + } + + configurations.runtimeClasspath { + attributes.attribute(transformedAttribute, true) + } + + abstract class CustomTransformAction implements TransformAction { + @InputArtifact abstract Provider getInputArtifact() + + @Override + void transform(TransformOutputs outputs) { + outputs.file(inputArtifact.get().asFile) + } + } + """ + .trimIndent() + ) + path("app/src/main/java/com/company/Main.java") + .writeText( + """ + package com.company; + + public class Main { + public static void main(String[] args) { + Utils.foo(); + } + } + """ + .trimIndent() + ) + + runWithSuccess(":app:$SHADOW_JAR_TASK_NAME") + + assertThat(jarPath("app/build/libs/app-all.jar")).useAll { + containsAtLeast("com/company/Main.class", "com/company/Utils.class", manifestEntry) + } + } + private fun dependencies(configuration: String, vararg flags: String): String { return runWithSuccess("dependencies", "--configuration", configuration, *flags).output } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt index 90d38555b..9a0208a4d 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/DependencyFilter.kt @@ -47,15 +47,19 @@ public interface DependencyFilter : Serializable { ) override fun resolve(configuration: Configuration): FileCollection { - val includes = mutableSetOf() - val excludes = mutableSetOf() - resolve( - dependencies = configuration.resolvedConfiguration.firstLevelModuleDependencies, - includedDependencies = includes, - excludedDependencies = excludes, - ) - return project.files(configuration.files) - - project.files(excludes.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) }) + return configuration - + project.files( + project.provider { + val includes = mutableSetOf() + val excludes = mutableSetOf() + resolve( + dependencies = configuration.resolvedConfiguration.firstLevelModuleDependencies, + includedDependencies = includes, + excludedDependencies = excludes, + ) + excludes.flatMap { it.moduleArtifacts.map(ResolvedArtifact::getFile) } + } + ) } override fun resolve(configurations: Collection): FileCollection { From 8da15782b567be331a56ea3a78ad6599db66f7fc Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Mar 2026 12:06:40 +0000 Subject: [PATCH 938/941] Allow opting out of adding shadowJar into assemble lifecycle (#1939) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Goooler <10363352+Goooler@users.noreply.github.com> Co-authored-by: Goooler --- api/shadow.api | 1 + docs/changes/README.md | 10 +++++++++ .../gradle/plugins/shadow/JavaPluginsTest.kt | 21 +++++++++++++++++++ .../gradle/plugins/shadow/ShadowBasePlugin.kt | 1 + .../gradle/plugins/shadow/ShadowExtension.kt | 8 +++++++ .../gradle/plugins/shadow/tasks/ShadowJar.kt | 5 ++++- .../plugins/shadow/ShadowPropertiesTest.kt | 1 + 7 files changed, 46 insertions(+), 1 deletion(-) diff --git a/api/shadow.api b/api/shadow.api index 0d90a6c24..46f49e233 100644 --- a/api/shadow.api +++ b/api/shadow.api @@ -41,6 +41,7 @@ public final class com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin$C } public abstract interface class com/github/jengelman/gradle/plugins/shadow/ShadowExtension { + public abstract fun getAddShadowJarToAssembleLifecycle ()Lorg/gradle/api/provider/Property; public abstract fun getAddShadowVariantIntoJavaComponent ()Lorg/gradle/api/provider/Property; public abstract fun getAddTargetJvmVersionAttribute ()Lorg/gradle/api/provider/Property; public abstract fun getBundlingAttribute ()Lorg/gradle/api/provider/Property; diff --git a/docs/changes/README.md b/docs/changes/README.md index 4379f310f..19eec4a8b 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,16 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.2...HEAD) - 2026-xx-xx +### Changed + +- Allow opting out of adding `shadowJar` into `assemble` lifecycle. ([#1939](https://github.com/GradleUp/shadow/pull/1939)) + ```kotlin + shadow { + // Disable making `assemble` task depend on `shadowJar`. This is enabled by default. + addShadowJarToAssembleLifecycle = false + } + ``` + ### Fixed - Fix interaction with Gradle artifact transforms. ([#1345](https://github.com/GradleUp/shadow/pull/1345)) diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 43d7cc948..2273c29f0 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -92,6 +92,27 @@ class JavaPluginsTest : BasePluginTest() { assertThat(result.output).contains("task dependencies: $SHADOW_JAR_TASK_NAME") } + @Issue("https://github.com/GradleUp/shadow/issues/1908") + @Test + fun shadowJarNotAddedToAssembleWhenDisabled() { + projectScript.appendText( + """ + shadow { + addShadowJarToAssembleLifecycle = false + } + """ + .trimIndent() + ) + + val result = runWithSuccess(ASSEMBLE_TASK_NAME) + + assertThat(result.task(":$ASSEMBLE_TASK_NAME")) + .isNotNull() + .transform { it.outcome } + .isEqualTo(SUCCESS) + assertThat(result.task(shadowJarPath)).isNull() + } + @Test fun shadowJarCliOptions() { val result = runWithSuccess("help", "--task", shadowJarPath) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt index 862c497c0..6c4f6fe8e 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowBasePlugin.kt @@ -20,6 +20,7 @@ public abstract class ShadowBasePlugin : Plugin { addShadowVariantIntoJavaComponent.convention(true) addTargetJvmVersionAttribute.convention(true) bundlingAttribute.convention(Bundling.SHADOWED) + addShadowJarToAssembleLifecycle.convention(true) } @Suppress("EagerGradleConfiguration") // this should be created eagerly. configurations.create(CONFIGURATION_NAME) diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt index 8cf5927c8..784dc4db7 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowExtension.kt @@ -32,4 +32,12 @@ public interface ShadowExtension { * Defaults to [Bundling.SHADOWED]. */ public val bundlingAttribute: Property + + /** + * If `true`, adds the `shadowJar` task as a dependency of the `assemble` lifecycle task. Set this + * to `false` if you don't want the `shadowJar` task to run when `assemble` is invoked. + * + * Defaults to `true`. + */ + public val addShadowJarToAssembleLifecycle: Property } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index 8ec8ba3c0..a3e65c159 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -1,6 +1,7 @@ package com.github.jengelman.gradle.plugins.shadow.tasks import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin +import com.github.jengelman.gradle.plugins.shadow.ShadowBasePlugin.Companion.shadow import com.github.jengelman.gradle.plugins.shadow.internal.DefaultDependencyFilter import com.github.jengelman.gradle.plugins.shadow.internal.DefaultInheritManifest import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFilter @@ -652,7 +653,9 @@ public abstract class ShadowJar : Jar() { // applied. // Using Spec applies the action to the task if it is added later. tasks.named(LifecycleBasePlugin.ASSEMBLE_TASK_NAME::equals).configureEach { - it.dependsOn(task) + if (shadow.addShadowJarToAssembleLifecycle.get()) { + it.dependsOn(task) + } } } } diff --git a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt index 38b38a1f6..d5bf5e8f4 100644 --- a/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt +++ b/src/test/kotlin/com/github/jengelman/gradle/plugins/shadow/ShadowPropertiesTest.kt @@ -67,6 +67,7 @@ class ShadowPropertiesTest { assertThat(addShadowVariantIntoJavaComponent.get()).isTrue() assertThat(addTargetJvmVersionAttribute.get()).isTrue() assertThat(bundlingAttribute.get()).isEqualTo(Bundling.SHADOWED) + assertThat(addShadowJarToAssembleLifecycle.get()).isTrue() } } From 09f50816c3927dba890b5871c880164b31572521 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Sun, 1 Mar 2026 23:31:18 +0800 Subject: [PATCH 939/941] Support Isolated Projects (#1139) * Add IP flag for all tests * First round * Cleanups * Update changelog * New `Project.getApiJars` * Reuse the config name * Call `incoming.artifacts` --- docs/changes/README.md | 4 + .../gradle/plugins/shadow/BasePluginTest.kt | 3 - .../gradle/plugins/shadow/JavaPluginsTest.kt | 3 +- .../plugins/shadow/internal/UnusedTracker.kt | 85 +++++++++---------- .../gradle/plugins/shadow/tasks/ShadowJar.kt | 3 +- .../plugins/shadow/testkit/GradleRunner.kt | 2 + 6 files changed, 49 insertions(+), 51 deletions(-) diff --git a/docs/changes/README.md b/docs/changes/README.md index 19eec4a8b..5fced3014 100644 --- a/docs/changes/README.md +++ b/docs/changes/README.md @@ -3,6 +3,10 @@ ## [Unreleased](https://github.com/GradleUp/shadow/compare/9.3.2...HEAD) - 2026-xx-xx +### Added + +- Support Isolated Projects. ([#1139](https://github.com/GradleUp/shadow/pull/1139)) + ### Changed - Allow opting out of adding `shadowJar` into `assemble` lifecycle. ([#1939](https://github.com/GradleUp/shadow/pull/1939)) 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 b69e5b854..bfc12d07e 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 @@ -415,9 +415,6 @@ abstract class BasePluginTest { const val runShadowTask = "tasks.named('$SHADOW_RUN_TASK_NAME', JavaExec)" const val jarTask = "tasks.named('jar', Jar)" - // TODO: enable this flag for all tests once we have fixed all issues with isolated projects. - // See https://github.com/GradleUp/shadow/pull/1139. - const val ipArgument = "-Dorg.gradle.unsafe.isolated-projects=true" const val infoArgument = "--info" fun String.toProperties(): Properties = Properties().apply { load(byteInputStream()) } diff --git a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt index 2273c29f0..3e1b32ab3 100644 --- a/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt +++ b/src/functionalTest/kotlin/com/github/jengelman/gradle/plugins/shadow/JavaPluginsTest.kt @@ -200,7 +200,7 @@ class JavaPluginsTest : BasePluginTest() { .appendText( """ sourceSets { - custom + create('custom') } dependencies { implementation sourceSets.custom.output @@ -871,7 +871,6 @@ class JavaPluginsTest : BasePluginTest() { val result = runWithSuccess( serverShadowJarPath, - ipArgument, infoArgument, "-P${ENABLE_DEVELOCITY_INTEGRATION_PROPERTY}=true", "-Dscan.dump", // Using scan.dump avoids actually publishing a Build Scan, writing it to a diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt index e9b09ba44..d7b7bd88b 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/internal/UnusedTracker.kt @@ -2,16 +2,51 @@ package com.github.jengelman.gradle.plugins.shadow.internal import java.io.File import org.gradle.api.Project -import org.gradle.api.artifacts.Configuration -import org.gradle.api.artifacts.Dependency -import org.gradle.api.artifacts.ExternalModuleDependency -import org.gradle.api.artifacts.FileCollectionDependency -import org.gradle.api.artifacts.ProjectDependency +import org.gradle.api.artifacts.component.ModuleComponentIdentifier +import org.gradle.api.attributes.Category +import org.gradle.api.attributes.LibraryElements +import org.gradle.api.attributes.Usage import org.gradle.api.file.FileCollection +import org.gradle.api.plugins.JavaPlugin.API_CONFIGURATION_NAME +import org.gradle.api.provider.Provider import org.gradle.api.tasks.InputFiles import org.vafer.jdependency.Clazzpath import org.vafer.jdependency.ClazzpathUnit +internal fun Project.getApiJars(): Provider> { + val apiConfiguration = + configurations.findByName(API_CONFIGURATION_NAME) ?: return provider { emptyList() } + + val configName = "shadowMinimizeApi" + val shadowApiConfig = + if (configurations.names.contains(configName)) { + configurations.named(configName) + } else { + configurations.register(configName) { + it.isCanBeResolved = true + it.isCanBeConsumed = false + it.attributes { attrs -> + attrs.attribute(Usage.USAGE_ATTRIBUTE, objects.named(Usage::class.java, Usage.JAVA_API)) + attrs.attribute( + Category.CATEGORY_ATTRIBUTE, + objects.named(Category::class.java, Category.LIBRARY), + ) + attrs.attribute( + LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, + objects.named(LibraryElements::class.java, LibraryElements.JAR), + ) + } + it.extendsFrom(apiConfiguration) + } + } + + return shadowApiConfig.flatMap { shadowApi -> + shadowApi.incoming.artifacts.resolvedArtifacts.map { artifacts -> + artifacts.filter { it.id.componentIdentifier !is ModuleComponentIdentifier }.map { it.file } + } + } +} + /** Tracks unused classes in the project classpath. */ internal class UnusedTracker( sourceSetsClassesDirs: Iterable, @@ -41,44 +76,4 @@ internal class UnusedTracker( cp.addClazzpathUnit(jarOrDir) } } - - companion object { - fun getApiJarsFromProject(project: Project): FileCollection { - val apiDependencies = - project.configurations.findByName("api")?.dependencies ?: return project.files() - val runtimeConfiguration = project.runtimeConfiguration - val apiJars = mutableListOf() - apiDependencies.forEach { dep -> - when (dep) { - is ProjectDependency -> { - apiJars.addAll(getApiJarsFromProject(project.project(dep.path))) - addJar(runtimeConfiguration, dep, apiJars) - } - is FileCollectionDependency -> { - apiJars.addAll(dep.files) - } - // Skip BOM dependencies and other non-JAR dependencies. - is ExternalModuleDependency -> Unit - else -> { - addJar(runtimeConfiguration, dep, apiJars) - val jarFile = - runtimeConfiguration.find { it.name.startsWith("${dep.name}-") } ?: return@forEach - apiJars.add(jarFile) - } - } - } - return project.files(apiJars) - } - - private fun addJar(config: Configuration, dep: Dependency, result: MutableList) { - config.find { isProjectDependencyFile(it, dep) }?.let { result.add(it) } - } - - private fun isProjectDependencyFile(file: File, dep: Dependency): Boolean { - val fileName = file.name - val dependencyName = dep.name - return fileName == "$dependencyName.jar" || - (fileName.startsWith("$dependencyName-") && fileName.endsWith(".jar")) - } - } } diff --git a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt index a3e65c159..ff4206809 100644 --- a/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt +++ b/src/main/kotlin/com/github/jengelman/gradle/plugins/shadow/tasks/ShadowJar.kt @@ -8,6 +8,7 @@ import com.github.jengelman.gradle.plugins.shadow.internal.MinimizeDependencyFil import com.github.jengelman.gradle.plugins.shadow.internal.UnusedTracker import com.github.jengelman.gradle.plugins.shadow.internal.classPathAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.fileCollection +import com.github.jengelman.gradle.plugins.shadow.internal.getApiJars import com.github.jengelman.gradle.plugins.shadow.internal.mainClassAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.multiReleaseAttributeKey import com.github.jengelman.gradle.plugins.shadow.internal.property @@ -108,7 +109,7 @@ public abstract class ShadowJar : Jar() { @get:Classpath public open val apiJars: ConfigurableFileCollection = objectFactory.fileCollection { - minimizeJar.map { if (it) UnusedTracker.getApiJarsFromProject(project) else emptySet() } + minimizeJar.map { if (it) project.getApiJars() else emptySet() } } @get:InputFiles diff --git a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt index 3b5707575..2175a5a3d 100644 --- a/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt +++ b/src/testKit/kotlin/com/github/jengelman/gradle/plugins/shadow/testkit/GradleRunner.kt @@ -28,6 +28,8 @@ val commonGradleArgs = "--stacktrace", // https://docs.gradle.org/current/userguide/configuration_cache.html#config_cache:usage:parallel "-Dorg.gradle.configuration-cache.parallel=true", + // https://docs.gradle.org/current/userguide/isolated_projects.html#how_do_i_use_it + "-Dorg.gradle.unsafe.isolated-projects=true", ) fun gradleRunner( From 6a52076e2e4385e786e79a16e1f81320fb7ace81 Mon Sep 17 00:00:00 2001 From: Zongle Wang Date: Mon, 2 Mar 2026 19:17:25 +0800 Subject: [PATCH 940/941] Change git push command for releasing (#1945) Updated git push command to use --follow-tags. --- RELEASING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASING.md b/RELEASING.md index f992e15ab..c83879b5d 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -30,7 +30,7 @@ 7. Push! ```sh - git push && git push --tags + git push --follow-tags ``` This will trigger a GitHub Action workflow which will create a GitHub release and upload the From 21fc71ef84cc6668197a3875a3f7783c88c7761b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:22:28 +0000 Subject: [PATCH 941/941] Initial plan